← Back to Kontasks
gvAgent Security Model - Full Documentation
Complete security model from gvAgentDesign.md Section 13 covering credential storage, token management
Output
gvAgent Security Model
From gvAgentDesign.md Section 13
13.1 Credential Storage
The SecretStore provides encrypted credential storage:
type SecretStore struct { path string masterKey []byte keyDeriver func(password string) []byte }
| Component | Implementation |
|---|---|
| Key Derivation | Argon2id |
| Parameters | time=3, memory=64MB, parallelism=4, keyLen=32 |
| Encryption | AES-256-GCM |
| Nonce | Prepended to ciphertext |
13.2 Provider Credential Management
Stored encrypted at ~/.config/gvagent/credentials.enc
providers: anthropic: api_key: "sk-ant-..." # Encrypted at rest openai: api_key: "sk-..." org_id: "org-..." google: api_key: "AIza..." hub: token: "gv_..." # Hub authentication token
13.3 Token Storage and Rotation
The TokenManager handles JWT tokens with auto-refresh:
type TokenManager struct { store *SecretStore hubClient *HubClient refreshAfter time.Duration } func (m *TokenManager) EnsureValidToken(ctx context.Context) (string, error) { token, err := m.store.Get("hub_token") if err != nil { return "", err } claims, err := m.parseToken(token) if err != nil { return "", err } // Auto-refresh before expiry if time.Until(claims.ExpiresAt) < m.refreshAfter { newToken, err := m.hubClient.RefreshToken(ctx, token) if err != nil { return "", err } m.store.Set("hub_token", newToken) return newToken, nil } return token, nil }
🛡️ Security Flow
Password/Passphrase │ ▼ ┌─────────────────┐ │ Argon2id │ → 64MB memory, 3 iterations └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 256-bit Key │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ AES-256-GCM │ → Authenticated encryption └────────┬────────┘ │ ▼ [nonce|ciphertext|tag] → credentials.enc
📝 Key Security Principles
- No plaintext secrets — All API keys encrypted at rest
- Memory-hard KDF — Argon2id resists GPU/ASIC attacks
- Authenticated encryption — GCM detects tampering
- Auto-rotation — Hub tokens refreshed before expiry
- Platform fallback — Uses OS keyring when available, encrypted file otherwise
What's Next?
Choose an action:
Quick Actions
Original Request
No layout configured
Details
Type
General
Status
Completed
Scope
vibetools
Tags
gvagentsecuritycredentialsencryption
Created
6 Jan 2026, 7:57 am
Updated
6 Jan 2026, 7:57 am
Timing
⏱️ 55.0s
0 tools
● TTFE 0%
● Thinking 100%
● Tools 0%
Created By
claude
Raw Data
{
"id": "a41c15b2-1f35-4ffc-a3d0-acbfd5035e40",
"type": "general",
"status": "completed",
"title": "gvAgent Security Model - Full Documentation",
"description": "Complete security model from gvAgentDesign.md Section 13 covering credential storage, token management",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:'Consolas','Monaco',monospace;\">\n<h2 style=\"font-family:sans-serif;color:#fff;margin:0 0 16px 0;\">gvAgent Security Model</h2>\n<p style=\"color:#9ca3af;margin:0 0 16px 0;\">From <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">gvAgentDesign.md</code> Section 13</p>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#98c379;margin:0 0 12px 0;\">13.1 Credential Storage</h3>\n<p style=\"color:#e0e0e0;margin:0 0 12px 0;\">The <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">SecretStore</code> provides encrypted credential storage:</p>\n<pre style=\"background:#3d3d3d;padding:12px;border-radius:4px;overflow-x:auto;line-height:1.6;margin:0;font-size:0.85rem;\">\n<span style=\"color:#c678dd;\">type</span> <span style=\"color:#e5c07b;\">SecretStore</span> <span style=\"color:#c678dd;\">struct</span> {\n path <span style=\"color:#56b6c2;\">string</span>\n masterKey []<span style=\"color:#56b6c2;\">byte</span>\n keyDeriver <span style=\"color:#c678dd;\">func</span>(password <span style=\"color:#56b6c2;\">string</span>) []<span style=\"color:#56b6c2;\">byte</span>\n}\n</pre>\n<table style=\"width:100%;border-collapse:collapse;margin-top:12px;\">\n<tr style=\"background:#3d3d3d;\"><th style=\"padding:8px;text-align:left;color:#fff;\">Component</th><th style=\"padding:8px;text-align:left;color:#fff;\">Implementation</th></tr>\n<tr><td style=\"padding:8px;color:#61afef;\">Key Derivation</td><td style=\"padding:8px;\"><span style=\"background:#22c55e;color:#fff;padding:2px 8px;border-radius:4px;\">Argon2id</span></td></tr>\n<tr><td style=\"padding:8px;color:#61afef;\">Parameters</td><td style=\"padding:8px;\">time=3, memory=64MB, parallelism=4, keyLen=32</td></tr>\n<tr><td style=\"padding:8px;color:#61afef;\">Encryption</td><td style=\"padding:8px;\"><span style=\"background:#3b82f6;color:#fff;padding:2px 8px;border-radius:4px;\">AES-256-GCM</span></td></tr>\n<tr><td style=\"padding:8px;color:#61afef;\">Nonce</td><td style=\"padding:8px;\">Prepended to ciphertext</td></tr>\n</table>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;\">13.2 Provider Credential Management</h3>\n<p style=\"color:#9ca3af;margin:0 0 12px 0;\">Stored encrypted at <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">~/.config/gvagent/credentials.enc</code></p>\n<pre style=\"background:#3d3d3d;padding:12px;border-radius:4px;overflow-x:auto;line-height:1.6;margin:0;font-size:0.85rem;\">\n<span style=\"color:#56b6c2;\">providers:</span>\n <span style=\"color:#e5c07b;\">anthropic:</span>\n <span style=\"color:#61afef;\">api_key:</span> <span style=\"color:#98c379;\">\"sk-ant-...\"</span> <span style=\"color:#7f848e;\"># Encrypted at rest</span>\n\n <span style=\"color:#e5c07b;\">openai:</span>\n <span style=\"color:#61afef;\">api_key:</span> <span style=\"color:#98c379;\">\"sk-...\"</span>\n <span style=\"color:#61afef;\">org_id:</span> <span style=\"color:#98c379;\">\"org-...\"</span>\n\n <span style=\"color:#e5c07b;\">google:</span>\n <span style=\"color:#61afef;\">api_key:</span> <span style=\"color:#98c379;\">\"AIza...\"</span>\n\n<span style=\"color:#56b6c2;\">hub:</span>\n <span style=\"color:#61afef;\">token:</span> <span style=\"color:#98c379;\">\"gv_...\"</span> <span style=\"color:#7f848e;\"># Hub authentication token</span>\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#61afef;margin:0 0 12px 0;\">13.3 Token Storage and Rotation</h3>\n<p style=\"color:#e0e0e0;margin:0 0 12px 0;\">The <code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">TokenManager</code> handles JWT tokens with auto-refresh:</p>\n<pre style=\"background:#3d3d3d;padding:12px;border-radius:4px;overflow-x:auto;line-height:1.6;margin:0;font-size:0.85rem;\">\n<span style=\"color:#c678dd;\">type</span> <span style=\"color:#e5c07b;\">TokenManager</span> <span style=\"color:#c678dd;\">struct</span> {\n store *SecretStore\n hubClient *HubClient\n refreshAfter <span style=\"color:#56b6c2;\">time.Duration</span>\n}\n\n<span style=\"color:#c678dd;\">func</span> (m *TokenManager) <span style=\"color:#61afef;\">EnsureValidToken</span>(ctx context.Context) (<span style=\"color:#56b6c2;\">string</span>, <span style=\"color:#56b6c2;\">error</span>) {\n token, err := m.store.<span style=\"color:#61afef;\">Get</span>(<span style=\"color:#98c379;\">\"hub_token\"</span>)\n <span style=\"color:#c678dd;\">if</span> err != <span style=\"color:#d19a66;\">nil</span> { <span style=\"color:#c678dd;\">return</span> <span style=\"color:#98c379;\">\"\"</span>, err }\n\n claims, err := m.<span style=\"color:#61afef;\">parseToken</span>(token)\n <span style=\"color:#c678dd;\">if</span> err != <span style=\"color:#d19a66;\">nil</span> { <span style=\"color:#c678dd;\">return</span> <span style=\"color:#98c379;\">\"\"</span>, err }\n\n <span style=\"color:#7f848e;\">// Auto-refresh before expiry</span>\n <span style=\"color:#c678dd;\">if</span> time.<span style=\"color:#61afef;\">Until</span>(claims.ExpiresAt) < m.refreshAfter {\n newToken, err := m.hubClient.<span style=\"color:#61afef;\">RefreshToken</span>(ctx, token)\n <span style=\"color:#c678dd;\">if</span> err != <span style=\"color:#d19a66;\">nil</span> { <span style=\"color:#c678dd;\">return</span> <span style=\"color:#98c379;\">\"\"</span>, err }\n m.store.<span style=\"color:#61afef;\">Set</span>(<span style=\"color:#98c379;\">\"hub_token\"</span>, newToken)\n <span style=\"color:#c678dd;\">return</span> newToken, <span style=\"color:#d19a66;\">nil</span>\n }\n <span style=\"color:#c678dd;\">return</span> token, <span style=\"color:#d19a66;\">nil</span>\n}\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#c678dd;margin:0 0 12px 0;\">🛡️ Security Flow</h3>\n<pre style=\"background:#3d3d3d;padding:12px;border-radius:4px;overflow-x:auto;line-height:1.4;margin:0;\">\n<span style=\"color:#7f848e;\">Password/Passphrase</span>\n │\n ▼\n<span style=\"color:#98c379;\">┌─────────────────┐</span>\n<span style=\"color:#98c379;\">│ Argon2id │</span> → 64MB memory, 3 iterations\n<span style=\"color:#98c379;\">└────────┬────────┘</span>\n │\n ▼\n<span style=\"color:#61afef;\">┌─────────────────┐</span>\n<span style=\"color:#61afef;\">│ 256-bit Key │</span>\n<span style=\"color:#61afef;\">└────────┬────────┘</span>\n │\n ▼\n<span style=\"color:#e5c07b;\">┌─────────────────┐</span>\n<span style=\"color:#e5c07b;\">│ AES-256-GCM │</span> → Authenticated encryption\n<span style=\"color:#e5c07b;\">└────────┬────────┘</span>\n │\n ▼\n<span style=\"color:#56b6c2;\">[nonce|ciphertext|tag]</span> → credentials.enc\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;\">\n<h4 style=\"margin:0 0 8px 0;color:#fff;\">📝 Key Security Principles</h4>\n<ul style=\"margin:0;padding-left:20px;color:#9ca3af;line-height:1.8;\">\n<li><strong>No plaintext secrets</strong> — All API keys encrypted at rest</li>\n<li><strong>Memory-hard KDF</strong> — Argon2id resists GPU/ASIC attacks</li>\n<li><strong>Authenticated encryption</strong> — GCM detects tampering</li>\n<li><strong>Auto-rotation</strong> — Hub tokens refreshed before expiry</li>\n<li><strong>Platform fallback</strong> — Uses OS keyring when available, encrypted file otherwise</li>\n</ul>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-top:16px;\">\n<h4 style=\"margin:0 0 8px 0;color:#fff;\">What's Next?</h4>\n<p style=\"color:#9ca3af;margin:0;\">Choose an action:</p>\n</div>\n</div>",
"requestedAt": "2026-01-06T12:20:00Z",
"requestId": "ebe11c32-c45d-4de1-9514-b1cd3b77270f",
"choices": [
{
"label": "Show guardrails",
"value": "Show me the gvAgent guardrails and permission system from gvAgentDesign.md Section 5",
"primary": true
},
{
"label": "Show config reference",
"value": "Show me the full configuration reference from gvAgentDesign.md Section 14"
},
{
"label": "Back to Linux secrets",
"value": "Show me more details about the Linux Secret Service D-Bus implementation"
}
],
"turnTiming": {
"totalMs": 55043,
"ttfeMs": 53,
"thinkingMs": 54990,
"toolExecutionMs": 0,
"toolCallCount": 0,
"thinkingPct": 100,
"toolsPct": 0,
"ttfePct": 0
}
},
"createdBy": "claude",
"createdAt": "2026-01-05T21:57:33.366Z",
"updatedAt": "2026-01-05T21:57:38.848Z",
"requestId": "ebe11c32-c45d-4de1-9514-b1cd3b77270f",
"scope": "vibetools",
"tags": [
"gvagent",
"security",
"credentials",
"encryption"
],
"targetUser": "claude"
}