Completed
on 6 Jan 2026, 7:53 am

Output

gvAgent Linux Secret Service

How the agent securely stores API keys and credentials on Linux

🔐 Secrets Backend Resolution

The agent uses a fallback chain to find the best available secrets backend:

func New() (SecretsStore, error) {
    backends := []func() (SecretsStore, error){
        // Priority order:
        tryKeychain,        // macOS Keychain (darwin only)
        tryCredentialMgr,   // Windows Credential Manager
        trySecretService,   // Linux secret service via D-Bus ⬅
        tryEncryptedFile,   // Fallback: encrypted file
    }
    for _, try := range backends {
        store, err := try()
        if err == nil { return store, nil }
    }
}

📡 Linux Secret Service (D-Bus)

Protocol:freedesktop.org Secret Service API
Transport:D-Bus session bus
Implementations:GNOME Keyring KDE Wallet
File:internal/secrets/secretservice_linux.go

When a user is logged into a GNOME or KDE desktop session, D-Bus is available and secrets are stored in the user's keyring — encrypted and unlocked by the user's login password.

📁 Fallback: Encrypted File

When D-Bus/Secret Service is unavailable (headless servers, Termux, SSH sessions):

Path:~/.local/share/gvagent/secrets.age
Encryption:AES-256-GCM or XChaCha20-Poly1305
Key derivation:Argon2id (or scrypt)
Dir mode:0700
File mode:0600

🛡️ SecretStore Interface

type SecretStore interface {
    Get(key string) (string, error)
    Set(key, value string) error
    Delete(key string) error
}

All platform backends implement this interface. Provider API keys (Claude, OpenAI, etc.) are stored via Set() and retrieved via Get().

📊 Platform Comparison

PlatformPrimary BackendFallback
macOSKeychainEncrypted file
WindowsCredential ManagerEncrypted file
Linux (desktop)Secret Service (D-Bus)Encrypted file
Linux (headless)(skipped)Encrypted file
Termux(no D-Bus)Encrypted file

What's Next?

Choose an action:

Quick Actions

Original Request

No layout configured

Details

Type General
Status Completed
Scope vibetools
Tags gvagentsecretslinuxsecurity
Created 6 Jan 2026, 7:53 am
Updated 6 Jan 2026, 7:53 am
Timing
⏱️ 60.4s
0 tools
TTFE 0% Thinking 100% Tools 0%
Created By claude

Raw Data

{
  "id": "72f130e8-699f-4189-a242-5960d8ae4d05",
  "type": "general",
  "status": "completed",
  "title": "gvAgent Linux Secret Service Explained",
  "description": "How gvAgent handles secrets storage on Linux via D-Bus Secret Service",
  "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 Linux Secret Service</h2>\n<p style=\"color:#9ca3af;margin:0 0 16px 0;\">How the agent securely stores API keys and credentials on Linux</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;\">🔐 Secrets Backend Resolution</h3>\n<p style=\"color:#e0e0e0;margin:0 0 12px 0;\">The agent uses a <strong>fallback chain</strong> to find the best available secrets backend:</p>\n<pre style=\"background:#3d3d3d;padding:12px;border-radius:4px;overflow-x:auto;line-height:1.8;margin:0;\">\n<span style=\"color:#c678dd;\">func</span> <span style=\"color:#61afef;\">New</span>() (SecretsStore, error) {\n    backends := []<span style=\"color:#c678dd;\">func</span>() (SecretsStore, error){\n        <span style=\"color:#7f848e;\">// Priority order:</span>\n        tryKeychain,        <span style=\"color:#7f848e;\">// macOS Keychain (darwin only)</span>\n        tryCredentialMgr,   <span style=\"color:#7f848e;\">// Windows Credential Manager</span>\n        <span style=\"color:#98c379;font-weight:bold;\">trySecretService</span>,   <span style=\"color:#7f848e;\">// Linux secret service via D-Bus ⬅</span>\n        tryEncryptedFile,   <span style=\"color:#7f848e;\">// Fallback: encrypted file</span>\n    }\n    <span style=\"color:#c678dd;\">for</span> _, try := <span style=\"color:#c678dd;\">range</span> backends {\n        store, err := try()\n        <span style=\"color:#c678dd;\">if</span> err == <span style=\"color:#d19a66;\">nil</span> { <span style=\"color:#c678dd;\">return</span> store, <span style=\"color:#d19a66;\">nil</span> }\n    }\n}\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;\">📡 Linux Secret Service (D-Bus)</h3>\n<table style=\"width:100%;border-collapse:collapse;\">\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;width:140px;\">Protocol:</td><td style=\"padding:8px;\"><a href=\"https://specifications.freedesktop.org/secret-service/\" style=\"color:#61afef;\">freedesktop.org Secret Service API</a></td></tr>\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;\">Transport:</td><td style=\"padding:8px;\">D-Bus session bus</td></tr>\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;\">Implementations:</td><td style=\"padding:8px;\"><span style=\"background:#3b82f6;color:#fff;padding:2px 8px;border-radius:4px;margin-right:6px;\">GNOME Keyring</span> <span style=\"background:#c678dd;color:#fff;padding:2px 8px;border-radius:4px;\">KDE Wallet</span></td></tr>\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;\">File:</td><td style=\"padding:8px;\"><code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">internal/secrets/secretservice_linux.go</code></td></tr>\n</table>\n<p style=\"color:#9ca3af;margin:12px 0 0 0;font-size:0.9rem;\">When a user is logged into a GNOME or KDE desktop session, D-Bus is available and secrets are stored in the user's keyring — encrypted and unlocked by the user's login password.</p>\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;\">📁 Fallback: Encrypted File</h3>\n<p style=\"color:#e0e0e0;margin:0 0 12px 0;\">When D-Bus/Secret Service is unavailable (headless servers, Termux, SSH sessions):</p>\n<table style=\"width:100%;border-collapse:collapse;\">\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;width:140px;\">Path:</td><td style=\"padding:8px;\"><code style=\"background:#3d3d3d;padding:2px 6px;border-radius:3px;\">~/.local/share/gvagent/secrets.age</code></td></tr>\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;\">Encryption:</td><td style=\"padding:8px;\">AES-256-GCM or XChaCha20-Poly1305</td></tr>\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;\">Key derivation:</td><td style=\"padding:8px;\">Argon2id (or scrypt)</td></tr>\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;\">Dir mode:</td><td style=\"padding:8px;\"><code>0700</code></td></tr>\n<tr><td style=\"padding:8px;color:#56b6c2;font-weight:bold;\">File mode:</td><td style=\"padding:8px;\"><code>0600</code></td></tr>\n</table>\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;\">🛡️ SecretStore Interface</h3>\n<pre style=\"background:#3d3d3d;padding:12px;border-radius:4px;overflow-x:auto;line-height:1.6;margin:0;\">\n<span style=\"color:#c678dd;\">type</span> <span style=\"color:#e5c07b;\">SecretStore</span> <span style=\"color:#c678dd;\">interface</span> {\n    <span style=\"color:#61afef;\">Get</span>(key <span style=\"color:#56b6c2;\">string</span>) (<span style=\"color:#56b6c2;\">string</span>, <span style=\"color:#56b6c2;\">error</span>)\n    <span style=\"color:#61afef;\">Set</span>(key, value <span style=\"color:#56b6c2;\">string</span>) <span style=\"color:#56b6c2;\">error</span>\n    <span style=\"color:#61afef;\">Delete</span>(key <span style=\"color:#56b6c2;\">string</span>) <span style=\"color:#56b6c2;\">error</span>\n}\n</pre>\n<p style=\"color:#9ca3af;margin:12px 0 0 0;font-size:0.9rem;\">All platform backends implement this interface. Provider API keys (Claude, OpenAI, etc.) are stored via <code>Set()</code> and retrieved via <code>Get()</code>.</p>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;\">\n<h4 style=\"margin:0 0 8px 0;color:#fff;\">📊 Platform Comparison</h4>\n<table style=\"width:100%;border-collapse:collapse;\">\n<tr style=\"background:#3d3d3d;\"><th style=\"padding:8px;text-align:left;color:#fff;\">Platform</th><th style=\"padding:8px;text-align:left;color:#fff;\">Primary Backend</th><th style=\"padding:8px;text-align:left;color:#fff;\">Fallback</th></tr>\n<tr><td style=\"padding:8px;\">macOS</td><td style=\"padding:8px;\">Keychain</td><td style=\"padding:8px;\">Encrypted file</td></tr>\n<tr><td style=\"padding:8px;\">Windows</td><td style=\"padding:8px;\">Credential Manager</td><td style=\"padding:8px;\">Encrypted file</td></tr>\n<tr style=\"background:#3d4d3d;\"><td style=\"padding:8px;color:#98c379;\">Linux (desktop)</td><td style=\"padding:8px;color:#98c379;\">Secret Service (D-Bus)</td><td style=\"padding:8px;\">Encrypted file</td></tr>\n<tr><td style=\"padding:8px;\">Linux (headless)</td><td style=\"padding:8px;color:#7f848e;\">(skipped)</td><td style=\"padding:8px;\">Encrypted file</td></tr>\n<tr><td style=\"padding:8px;\">Termux</td><td style=\"padding:8px;color:#7f848e;\">(no D-Bus)</td><td style=\"padding:8px;\">Encrypted file</td></tr>\n</table>\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:15:00Z",
    "requestId": "81dba387-e742-4524-bd61-465ee10ce699",
    "choices": [
      {
        "label": "Show security model",
        "value": "Show me the full gvAgent security model from gvAgentDesign.md Section 13",
        "primary": true
      },
      {
        "label": "Show encrypted file details",
        "value": "Explain the encrypted file fallback in more detail - how is the master key derived?"
      },
      {
        "label": "Back to overview",
        "value": "Show me the gvAgent documentation coverage summary again"
      }
    ],
    "turnTiming": {
      "totalMs": 60444,
      "ttfeMs": 58,
      "thinkingMs": 60386,
      "toolExecutionMs": 0,
      "toolCallCount": 0,
      "thinkingPct": 100,
      "toolsPct": 0,
      "ttfePct": 0
    }
  },
  "createdBy": "claude",
  "createdAt": "2026-01-05T21:53:28.276Z",
  "updatedAt": "2026-01-05T21:53:34.515Z",
  "requestId": "81dba387-e742-4524-bd61-465ee10ce699",
  "scope": "vibetools",
  "tags": [
    "gvagent",
    "secrets",
    "linux",
    "security"
  ],
  "targetUser": "claude"
}
DashboardReportsKontasksSessionsTelemetryLogs + Go