Skip to content

Arquitetura

O Keyholder se apoia no OpenBao em vez de ter um cofre próprio. O OpenBao guarda e criptografa os segredos, emite tokens de curta duração, mantém leases, aplica políticas, oferece login com TOTP e uma interface web. O Keyholder acrescenta a única peça que falta: um servidor MCP e um runner que usam a credencial pelo agente sem devolvê-la ao agente.

Componentes

flowchart TB
    subgraph agentside ["Lado do agente"]
      client["Cliente MCP<br/>(Claude Code, Cursor, Codex)"]
      kh["keyholder-mcp<br/>token só em memória"]
      child["comando permitido<br/>(processo filho)"]
    end
    subgraph vaultside ["Lado do cofre"]
      bao["OpenBao<br/>KV v2, tokens, leases, auditoria"]
    end
    human["Você<br/>(interface web ou CLI bao, TOTP)"]
    client -- "stdio: tools, só nomes" --> kh
    kh -- "HTTP: desembrulhar, ler, listar" --> bao
    kh -- "exec, sem shell,<br/>ambiente limpo" --> child
    child --> ext["API / nuvem"]
    human -- "guardar segredos,<br/>emitir token embrulhado" --> bao
Componente Origem Papel
OpenBao existente (openbao.org) Segredos, tokens com TTL, leases, políticas, TOTP, interface web, auditoria
keyholder-mcp este projeto Tools MCP, late binding, mascaramento, lista de comandos, escopo
khcall este projeto Cliente de depuração e motor dos tutoriais

Fluxo de acesso

sequenceDiagram
    actor Voce as Você
    participant Bao as OpenBao
    participant KH as keyholder-mcp
    participant Agente
    Voce->>Bao: login (userpass/OIDC + TOTP)
    Voce->>Bao: token create -ttl -wrap-ttl
    Bao-->>Voce: token embrulhado (uso único)
    Voce->>KH: inicia com BAO_WRAPPING_TOKEN
    KH->>Bao: desembrulha
    Bao-->>KH: token de cliente (só em memória)
    Agente->>KH: keyholder_run {{vault://...}}
    KH->>Bao: lê o campo
    KH->>KH: executa o comando, mascara a saída
    KH-->>Agente: saída mascarada
    Note over KH,Bao: Fim do TTL: leituras dão 403,<br/>as tools pedem renovação fora do chat

Pacotes

Caminho Responsabilidade Depende de
cmd/keyholder-mcp Lê a configuração, abre a sessão, registra as tools MCP tudo abaixo, MCP Go SDK
cmd/khcall Cliente MCP mínimo MCP Go SDK
internal/runner Lista de comandos, expansão dos placeholders, exec sem shell, filtro de ambiente, limites ref, mask
internal/bao Cliente OpenBao: desembrulho, leitura no KV v2, listagem, status, erros limpos ref, cliente OpenBao
internal/ref Interpretação de vault://caminho/CHAVE e {{vault://...}}, checagem de escopo biblioteca padrão
internal/mask Troca valores conhecidos por [hidden:<ref>] biblioteca padrão

internal/runner, internal/ref e internal/mask nunca importam o OpenBao nem o MCP. O runner recebe uma função runner.Resolver, e só o cmd/keyholder-mcp liga bao.Store.Resolve a ela. Um novo backend (outro cofre, credenciais dinâmicas de nuvem) é um novo resolver, não uma mudança no runner. A revisão de código cobra essa direção (solid#solid-d, veja Revisão de código).

Stack

Camada Escolha Por quê
Linguagem Go 1.25 Um binário estático por sistema; o próprio OpenBao é Go
MCP modelcontextprotocol/go-sdk v1.8.0 SDK oficial, transporte stdio
Cliente do cofre openbao/openbao/api/v2 v2.7.1 Cliente oficial: desembrulho, KV v2, consulta ao token
Execução os/exec Lista de argumentos, sem shell
Cofre OpenBao, KV v2 Código aberto (MPL-2.0), Linux Foundation