The wallet daemon provides background services for the wallet-cli, including:
- gRPC Server - Inter-process communication for wallet operations
- MCP Server - Model Context Protocol integration for AI assistants
- User Approval Flow - Pinentry-based approval for sensitive operations
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ wallet-cli │ │ AI Assistant │ │ Other Apps │
│ (commands) │ │ (Claude, etc) │ │ │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌────────────┴────────────┐
│ Unix Socket │
│ ~/.config/wallet-cli/ │
│ daemon.sock │
└────────────┬────────────┘
│
┌────────────┴────────────┐
│ Wallet Daemon │
│ │
│ ┌─────────────────┐ │
│ │ gRPC Server │ │
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ MCP Server │ │
│ │ (stdio) │ │
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ Keystore │ │
│ │ (in-memory) │ │
│ └─────────────────┘ │
└─────────────────────────┘
│
┌────────────┴────────────┐
│ FIDO2 Device │
│ (YubiKey, etc.) │
└─────────────────────────┘
# Start the daemon (foreground)
wallet-cli daemon start
# Start with MCP server enabled
wallet-cli daemon start --mcp
# Start in foreground mode (for debugging)
wallet-cli daemon start --foreground# Check daemon status
wallet-cli daemon status
# Stop the daemon
wallet-cli daemon stop
# Unlock the keystore (requires security key)
wallet-cli daemon unlock
# Lock the keystore
wallet-cli daemon lockFor automatic startup, use the provided systemd user service:
# Copy service files (done automatically by Debian package)
cp debian/wallet-cli-daemon.service ~/.config/systemd/user/
cp debian/wallet-cli-daemon.socket ~/.config/systemd/user/
# Reload systemd
systemctl --user daemon-reload
# Enable and start the service
systemctl --user enable wallet-cli-daemon
systemctl --user start wallet-cli-daemon
# Check status
systemctl --user status wallet-cli-daemon
# View logs
journalctl --user -u wallet-cli-daemon -fThe daemon includes an MCP (Model Context Protocol) server for AI assistant integration.
Add to ~/.config/claude/claude_desktop_config.json:
{
"mcpServers": {
"wallet": {
"command": "wallet-cli",
"args": ["daemon", "start", "--mcp"],
"env": {}
}
}
}The MCP server exposes the following tools to AI assistants:
| Tool | Description |
|---|---|
wallet_status |
Get daemon and keystore status |
wallet_list_keys |
List available signing keys |
wallet_sign |
Sign data with a wallet key |
Sensitive operations (like signing) require user approval via pinentry:
- AI assistant requests a signing operation
- Daemon displays pinentry dialog with operation details
- User approves or denies
- Result returned to AI assistant
The approval dialog shows:
- Operation type (e.g., "sign", "present credential")
- Description of what's being signed
- Approve/Deny buttons
The daemon socket is created with mode 0600 (owner only):
- Location:
~/.config/wallet-cli/daemon.sockor$XDG_RUNTIME_DIR/wallet-cli/daemon.sock - Only the owner can connect
- Private keys are derived from FIDO2 PRF and held in memory
- Auto-lock after configurable timeout (default: 5 minutes)
- Never written to disk
- All signing operations from MCP clients require explicit user approval
- Uses system pinentry for secure UI
- 60-second timeout for approval dialogs
The daemon exposes a gRPC API at the Unix socket:
service WalletDaemon {
// Status returns daemon status
rpc Status(StatusRequest) returns (StatusResponse);
// Lock immediately locks the keystore
rpc Lock(LockRequest) returns (LockResponse);
// ListKeys returns available signing keys
rpc ListKeys(ListKeysRequest) returns (ListKeysResponse);
// SignJWT signs a JWT with the specified key
rpc SignJWT(SignJWTRequest) returns (SignJWTResponse);
// Sign performs raw signing with the specified key
rpc Sign(SignRequest) returns (SignResponse);
// GetApproval requests user approval for an operation
rpc GetApproval(GetApprovalRequest) returns (GetApprovalResponse);
}See api/proto/daemon/v1/wallet.proto for full API definitions.
# Check if already running
wallet-cli daemon status
# Check socket file
ls -la ~/.config/wallet-cli/daemon.sock
# Remove stale socket
rm ~/.config/wallet-cli/daemon.sock# Test MCP directly
wallet-cli daemon start --mcp --foreground
# Check Claude Desktop logs
tail -f ~/.config/claude/logs/mcp.logInstall a pinentry program:
# GNOME
sudo apt install pinentry-gnome3
# KDE
sudo apt install pinentry-qt
# TTY fallback
sudo apt install pinentry-curses# Check socket permissions
ls -la ~/.config/wallet-cli/daemon.sock
# Should be -rw------- (0600)
# Owner should be your userEnvironment variables:
| Variable | Description | Default |
|---|---|---|
WALLET_DAEMON_SOCKET |
Socket path | ~/.config/wallet-cli/daemon.sock |
WALLET_DAEMON_TIMEOUT |
Auto-lock timeout (seconds) | 300 |
PINENTRY_PROGRAM |
Path to pinentry | Auto-detected |
# Build with daemon support
make build
# Run tests
make test
# Test daemon specifically
go test -v ./internal/daemon/...# Run daemon with verbose logging
wallet-cli --debug daemon start --foreground
# Test gRPC connection
grpcurl -plaintext -unix ~/.config/wallet-cli/daemon.sock siros.daemon.v1.WalletDaemon/Status