Documentation menu
Reference

The Kvendra CLI

Pre-alpha. The CLI is under active development — the kvendra binary is installed from source, the crates.io name is reserved but not yet published, and a Homebrew formula and signed builds are planned. Treat commands and defaults below as the current shape, not a frozen contract.

The CLI is where Kvendra meets your machine. It is a foundational security layer — but optional to get started: the Platform and the Skills work without it, and you add it only when you want your agent to run real ops (a git push, a deploy) without exposing your tokens. It is a single Rust binary that does two jobs at once: it keeps your secrets in a local zero-knowledge vault, and it acts as an MCP capability broker that runs sensitive operations on behalf of a tool or agent — without ever handing the underlying credential to that caller.

The Kvendra CLI is a foundational security layer — optional to get started: a Rust (edition 2024, MSRV 1.85+) binary (kvendra, Apache-2.0).

The kvendra CLI is a Rust binary: a foundational security layer that is optional to get started. It is a zero-knowledge local vault (Level 2) plus an MCP capability broker, an HMAC-signed audit log, a detection layer and a TUI — you add it when you want your agent to run real operations without exposing your tokens.

The open-core primitives

The broker exposes 7 open-core primitives. Each primitive receives an injected secret resolved by the vault — the model issuing the call never sees the raw value.

PrimitiveOperationsNotes
kvendra.gitclone, push, pull, commit, tagSSH-only authentication by threat model — no inline tokens.
kvendra.githubread_repo, read_issue, update_issue, update_repo, release, add_topics, create_issue, list_issuesRead ops are auth-light; create_issue is destructive and body-sanitized.
kvendra.npmpublish, deprecate, read_metadataPackage registry operations.
kvendra.pypiupload, read_metadataPython package registry operations.
kvendra.httprequestA single generic outbound HTTP request primitive.
kvendra.shellexecRun an allowlisted shell command.

Beyond these, an audit-marked escape hatch exists: kvendra.unsafe.raw_token — an audit-marked escape hatch, always flagged unsafe in the audit log.

Introspection

kvendra capabilities (and —pretty) emits a canonical JSON manifest of the broker’s primitive surface: broker_version, schema_version, and each primitive with its ops, destructive ops and vault profile pattern. Auth-less, read-only, no vault unlock required.

The vault

The vault lives in a per-user home directory and holds: a config file (HMAC sidecar), a SQLite audit database (WAL, HMAC-chained), Argon2id-hashed recovery codes, a sentinel blob, AES-256-GCM secret blobs per profile, and HMAC-signed allowlist YAMLs per profile.

Cryptography: Argon2id KDF, AES-256-GCM AEAD, an HMAC chain over the audit log, a sentinel blob and recovery codes.

Modes of operation: Local (default): zero-knowledge vault, secrets resolved locally. A remote-broker resolver mode exists for hosted deployments via OIDC PKCE login.

What it is, in one idea

The design separates the thing that decides what to do (the LLM) from the thing that holds the keys (the vault). The model never sees a token. It calls a primitive over MCP; the broker resolves the secret, runs the operation in a subprocess outside the model's context, and returns only a sanitized result. The credential is used, but never exposed to the caller that asked for it.

The user flow, step by step

1. Install

Build from source while the project is pre-alpha:

cargo install --git https://github.com/KvendraAI/kvendra-cli kvendra

That gives you the kvendra binary. (Not on crates.io yet; brew is pending.)

2. kvendra init — create the vault (once)

Run this a single time to set up your local vault. It establishes:

  • a master password, from which the encryption key is derived with Argon2id (a memory-hard KDF);
  • a BIP-39 recovery phrase — 12 words, shown exactly once — for recovering access if you forget the master password;
  • 8 single-use recovery codes for critical actions.

Save the recovery material when it is shown — it is never displayed again. Nothing in clear text touches the disk: the vault stores only encrypted blobs.

3. Profiles — add a set of credentials

kvendra secret add <profile_id> --secret-file <path>

A profile is one named set of credentials, encrypted with AES-256-GCM and stored as a per-profile blob in the vault directory. Each profile carries a type — github_pat, npm_token, aws_credentials, generic, and so on — so the broker knows how to inject it.

4. Allowlist policies — declare what is permitted

Each profile gets a YAML allowlist, signed with HMAC, that declares precisely which operations and parameters the broker will run for it. Per primitive, for example:

  • git — allowed repos / refs, forbidden args;
  • shell — allowed binaries, argument constraints, a working-directory pattern;
  • http — a URL pattern (regex) and forbidden headers.

Defaults are restrictive: anything ambiguous or not explicitly allowed is rejected. The HMAC signature is re-verified on every broker start, so an allowlist edited out of band is detected rather than silently honoured.

5. kvendra unlock — in your terminal, not the agent

Unlocking derives the key from your master password and writes a machine-bound session — tied to this host, user and home — with a time-to-live. You run this in a real terminal; the broker requires a genuine TTY, so a captured environment or an MCP client cannot unlock the vault on its own.

  • Default TTL is 4 hours; the configurable maximum is 24 hours.
  • Flags include --ttl (set the lifetime), --extend (refresh an active session), --no-keychain, and --recover (reset the master password via the BIP-39 phrase).

6. Use it with Claude Code

The MCP client launches kvendra mcp serve over stdio. The broker reads the existing unlocked session — the master password never enters the MCP process. When the LLM calls a primitive, the vault resolves the secret and injects it only into the operation's subprocess (an environment variable or an ephemeral credential helper). The response passes through a detection/sanitization layer and is returned to the agent with the credential stripped out.

# Inspect the broker's primitive surface — no unlock required
kvendra capabilities --pretty

The seven primitives (plus an escape hatch)

The broker exposes seven open-core capability primitives — kvendra.git, kvendra.github, kvendra.npm, kvendra.pypi, kvendra.aws, kvendra.http and kvendra.shell (their exact operations are in the manifest above) — plus one escape hatch:

  • kvendra.unsafe.raw_token — explicit opt-in, rate-limited per session, requires a stated reason, and is always flagged in the audit log. It is the last resort when no structured primitive fits, not a normal path.

Approval modes

You choose how much the broker prompts before acting:

  • silent — never prompts (for non-interactive contexts);
  • ask — prompts on every call;
  • ask-destructivethe default: prompts only for operations the manifest marks destructive.

Under MCP the prompt is a native operating-system modal (macOS today; native Touch ID is pre-alpha). The mode is configurable by environment variable, per profile, or in global config — the most specific setting wins.

Break-glass vs. the escape hatch

These are two different things, and the distinction matters:

  • Break-glass (kvendra bypass / kvendra protect) temporarily grants a capability in workspace mode. Grants are signed with ed25519, scoped to a specific primitive.op with a TTL, and revocable with protect. It widens what is allowed, briefly and accountably.
  • The escape hatch (kvendra.unsafe.raw_token) hands a raw token to the caller. It does not widen an allowlist; it bypasses the whole brokering model for one call, which is why it is opt-in, rate-limited and always flagged.

Audit and recovery

Every brokered operation is written to a local HMAC-chained audit log (SQLite), where each entry commits to the one before it. You can verify the chain end to end:

kvendra audit --verify   # detects tampering row by row

If access is ever lost, recovery has two layers: the BIP-39 phrase resets the master password, and the single-use recovery codes authorize critical actions.