Service Architecture
ConnectionService
Location:services/connection.goWails Binding:
github.com/felixdotgo/querybox/services.ConnectionService
Manages the lifecycle of database connections, including CRUD operations, credential storage, and event emission.
Data Model
credential_key is not the actual credential—it’s a reference to the secret stored in CredManager.
Public API
ListConnections
services/connection.go:177
CreateConnection
name: Display name for the connectiondriverType: Plugin identifier (must match a discovered plugin)credential: JSON-encoded credential blob (structure defined by plugin’s auth forms)
- Generate UUID for connection ID
- Derive credential key:
"connection:<uuid>" - Store credential via
CredManager.Store(key, credential) - Insert metadata into
connections.db - Emit
connection:createdevent with full Connection object
services/connection.go:233
Emits: connection:created event → Frontend updates connection list reactively (no re-fetch needed)
GetConnection
services/connection.go:208
GetCredential
- Look up connection metadata to get
credential_key - Call
CredManager.Get(credential_key) - Return raw credential string
services/connection.go:273
DeleteConnection
- Look up
credential_keyfrom connection metadata - Best-effort delete from CredManager (ignores errors)
- Delete row from
connections.db - Emit
connection:deletedevent with connection ID
services/connection.go:299
Emits: connection:deleted event → Frontend removes connection from list
Database Schema
querybox/connections.db
Migration: Legacy credential_blob column migrated to keyring on service initialization (services/connection.go:113)
PluginManager
Location:services/pluginmgr/pluginmgr.goWails Binding:
github.com/felixdotgo/querybox/services/pluginmgr.Manager
Discovery and on-demand execution of database driver plugins.
Plugin Discovery
Scan Locations (in order of precedence):- User directory:
<UserConfigDir>/querybox/plugins/ - Bundled directory:
<executable_dir>/bin/plugins/
pluginmgr.go:264):
- Find all executable files in scan directories
- Probe each with
plugin infocommand (5s timeout) - Parse JSON response to populate metadata
- Store in in-memory registry (
map[string]PluginInfo) - Emit
plugins:readyevent when complete
Plugin Info
Public API
ListPlugins
pluginmgr.go:456
Rescan
pluginmgr.go:610
ExecPlugin
name: Plugin ID (filename)connection: Credential map (typically parsed fromGetCredential())query: Query string (SQL, NoSQL command, etc.)options: Optional flags (e.g.,{"explain-query": "yes"})
- Look up plugin path in registry
- Spawn subprocess:
<plugin> exec - Write JSON request to stdin:
- Read protobuf-JSON response from stdout (30s timeout)
- Parse into
plugin.ExecResponse - Terminate subprocess
pluginmgr.go:474
GetConnectionTree
- Spawn
<plugin> connection-tree - Write JSON request:
{"connection": {...}} - Read protobuf-JSON response (30s timeout)
- Parse into
ConnectionTreeResponse
pluginmgr.go:621
ExecTreeAction
ExecPlugin(). Used when a tree node action is clicked.
Implementation: pluginmgr.go:696
TestConnection
- Spawn
<plugin> test-connection - Write JSON request:
{"connection": {...}} - Read response (15s timeout)
- Parse into
TestConnectionResponse
pluginmgr.go:706
GetPluginAuthForms
- Spawn
<plugin> authforms - Read protobuf-JSON response (2s timeout)
- Parse into
map[formID]*AuthForm
pluginmgr.go:776
CredManager
Location:services/credmanager/credmanager.goWails Binding: None (internal service) Provides secure credential storage with automatic fallback when OS keyring is unavailable.
Architecture
Credential Backends
| Backend | Platform Support | Persistence | Use Case |
|---|---|---|---|
| OS Keyring | macOS: Keychain Windows: Credential Manager Linux: libsecret, KWallet | ✓ Persistent, encrypted | Default for desktop |
| SQLite | All platforms | ✓ Persistent, plaintext† | Headless servers, containers |
| Memory | All platforms | ✗ Session-only | Filesystem unavailable |
Public API
Store
credmanager.go:114
Get
"secret not found" error if key doesn’t exist.
Implementation: credmanager.go:135
Delete
credmanager.go:159
Backend
"keyring", "sqlite", or "memory".
Implementation: credmanager.go:177
Keyring Probing
On initialization, CredManager tests keyring availability:credmanager.go:49
If the probe fails, CredManager falls back to SQLite.
SQLite Schema
<UserConfigDir>/querybox/credentials.db
Service Lifecycle
Initialization (main.go)
Shutdown
Wails callsShutdown() on all bound services when the app quits:
- ConnectionService: Closes SQLite connection (
connection.go:140) - PluginManager: No-op (no background processes)
- CredManager: Call
Close()manually if needed (not automatic)
Error Handling
Service Errors
All public methods returnerror as the last return value. Frontend bindings surface these as rejected promises.
Frontend Example:
Plugin Errors
Plugin failures are captured in the response:Event Emission Errors
Event emission is nil-safe. Ifapp is nil (e.g., in tests), events are silently dropped.
Testing
Unit Tests
Services are designed to be testable without Wails:Mocking
Internal dependencies use function variables for test injection:credmanager/credmanager_test.goconnection_test.gopluginmgr/pluginmgr_test.go
Next Steps
- System Architecture Overview - High-level system design
- Frontend Architecture - Vue 3 components and composables
- Event System - Event contracts and patterns