Agent-safe secret runtime — broker operations through
kxxxso LLMs and agentic tools never see raw credentials.
# Install
brew tap kxxx-dev/kxxx && brew install kxxx
# Store a secret
kxxx set env/GITHUB_TOKEN --stdin < ~/.secrets/github-token
# Allow the broker to act on a specific repo
mkdir -p ~/.config/kxxx/broker
echo "octo/repo" > ~/.config/kxxx/broker/github.create_issue.repos
# Agent-safe: broker an operation without exposing the secret
ref="$(kxxx ref env/GITHUB_TOKEN --service kxxx.secrets)"
kxxx broker github.create_issue --service kxxx.secrets --ref "$ref" --repo octo/repo --title "hello"kxxx is a secret runtime for local developer workflows that is being repositioned around an agent-safe execution model.
Today, it can still do familiar compatibility-path work such as resolving secrets, exporting env vars, and launching child processes with injected secrets. But the intended direction is different: new integrations should prefer a brokered safe path where kxxx remains the policy and secret-resolution boundary, and the caller receives only the minimum result metadata needed to continue.
That distinction matters because a “better keychain CLI” is not the real goal. The project is moving toward a model where storage backend choice, secret identity, policy, and audit all support safer agent/tool execution by default. The threat model and invariants that define that direction live in ADR 0001. This README is the contributor-facing overview of what exists today and what direction the project is taking.
- It stores and retrieves secrets for local workflows.
- It separates logical secret descriptors such as
env/OPENAI_API_KEYfrom opaqueSecretRefidentifiers. - It supports backend selection behind a common CLI surface.
- It exposes one narrow brokered safe path today:
kxxx broker github.create_issue. - It keeps existing compatibility flows available for users who still need env-materializing behavior.
The long-term direction is an agent-safe secret runtime, not just a storage abstraction layer.
- The preferred safe path should avoid showing raw secret values to an LLM, agent, or other tool-using caller.
- Secret identity should stay distinct from env var naming so policy and audit can reason about explicit references instead of implicit process state.
- Backend/provider strategy should support both interactive desktop workflows and headless/non-interactive environments without treating macOS keychain behavior as the architecture.
- Policy and audit should be part of the runtime boundary, not bolt-ons after storage is generalized.
The current brokered GitHub issue flow is a proof point for that direction, not the finished product.
kxxx currently has two clearly different usage modes.
The preferred safe path is broker-oriented.
- The caller passes an opaque
SecretRefand operation arguments. kxxxevaluates policy before resolving the secret or performing the provider action.kxxxresolves the secret internally and returns only brokered result metadata.- Structured broker audit records capture the action without emitting the raw secret.
Today, the narrow safe-path MVP is:
ref="$(kxxx ref env/GITHUB_TOKEN --service kxxx.secrets)"
kxxx broker github.create_issue \
--service kxxx.secrets \
--ref "$ref" \
--repo octo/repo \
--title "hello"This flow is intentionally limited to one provider operation, one policy shape, and one audit format. See docs/SAFE_PATH_MVP.md for the exact slice boundary.
Compatibility-path commands remain available for existing workflows:
kxxx getkxxx envkxxx run
These commands can materialize raw secret values to stdout or to a child-process environment. They remain supported because users still need them, but they are not the preferred direction for new integrations.
Example compatibility flow:
kxxx run --repo auto -- npm run devkxxx now routes persistent storage through a backend layer instead of calling macOS keychain helpers directly from business logic.
Current backend state:
autois the default selection.darwin-keychainpreserves the currentsecurity/ksbehavior on supported macOS environments.encrypted-fileis the current headless-safe persistent backend and requiresKXXX_ENCRYPTED_FILE_KEY.memoryexists for tests and internal same-process scenarios only; it is not intended as a normal CLI backend.secret-serviceandwincredare named backend targets but are not implemented yet.
Important caveat:
autois platform-based today, not fully headless-aware. On headless or non-interactive macOS environments, prefer--backend encrypted-fileorKXXX_BACKEND=encrypted-fileexplicitly.
The safe path also has a provider side, but that layer is intentionally narrow right now. The only brokered provider operation currently exposed is github.create_issue.
One of the core architectural changes in kxxx is that secret identity is no longer tied to env var names.
- A descriptor such as
env/OPENAI_API_KEYorapp/my-repo/API_TOKENis a logical binding used for compatibility and migration. - A
SecretRefsuch assecretref:v1:encrypted-file:...is the opaque identity used by the safe path. - This split allows policy, audit, and backend choice to reason about secrets without making env var naming the primary storage model.
That distinction is already visible in the CLI:
kxxx set env/OPENAI_API_KEY --stdin < ~/.secrets/openai
kxxx ref env/OPENAI_API_KEY --service kxxx.secretsThe full threat model lives in ADR 0001. The short version is:
- The preferred safe path should not require the caller, LLM, or child process to see the raw secret value.
- Compatibility-path commands are explicit exceptions and remain secondary.
- If a brokered operation has policy, policy is evaluated before secret resolution or provider execution.
- Raw secret values must not appear in stdout, stderr, or structured safe-path audit events.
- Audit may still contain sanitized metadata such as opaque refs, backend identifiers, target resources, and process context.
- Interactive desktop keyrings and headless/non-interactive environments have different trust assumptions and should not be conflated.
Existing users do not need to abandon current workflows all at once.
- If you already use
get,env, orrun, those commands still exist and remain the compatibility path. - If you previously thought in terms of env-style secret names only, start by using
kxxx refto discover the corresponding opaqueSecretRef. - If you need a persistent backend in CI or other headless environments, prefer
encrypted-fileand supplyKXXX_ENCRYPTED_FILE_KEY. - If you are moving from older keychain service names, continue using
migrate serviceandmigrate importto consolidate into the current layout.
Example headless-safe persistent setup:
export KXXX_ENCRYPTED_FILE_KEY="replace-me"
kxxx set env/GITHUB_TOKEN --service kxxx.secrets --backend encrypted-file --stdin < ~/.secrets/github-tokenThe CLI now has three main groups:
- secret management and compatibility flows:
set,ref,get,list,env,run - brokered safe-path flows:
broker github.create_issue,broker audit - migration and scanning flows:
migrate import,migrate service,audit
Defaults:
- service:
kxxx.secrets - backend:
auto - repo detection:
git rev-parse --show-toplevelbasename, fallback to current directory basename - audit roots (auto):
~/src,~/.config
For the exact command syntax, use kxxx --help.
kxxx is still early in the transition from “developer secrets CLI” to “agent-safe secret runtime.”
What exists now:
- threat model and invariants
- opaque secret references
- backend abstraction with a headless-safe persistent option
- one brokered provider operation with policy and audit
What remains open:
- broader provider coverage beyond
github.create_issue - richer policy models beyond the current exact repo allowlist
- real platform implementations for
secret-serviceandwincred - clearer headless strategy on macOS when
autoselection is not sufficient - how far compatibility-path flows should continue to evolve versus stabilize
brew tap kxxx-dev/kxxx
brew install kxxxkxxxrequires Bash 4.3 or later.- On macOS,
bats testcan run test files under/bin/bash3.2 because the test shebang uses#!/usr/bin/env bash. - Prefer
bin/test, which prepends a compatible Homebrew Bash before invokingbats.
bin/test