← Field log

Every agent needs a wallet

Right now, the way agents get credentials is embarrassing.

You paste API keys into .env files. You share them over Slack. You hardcode them in prompts. You copy the same OpenAI key into Claude Code, Cursor, your CI pipeline, and three different agent frameworks. When you rotate a key, you update it in seven places and miss two.

GitGuardian found 29 million hardcoded secrets in public GitHub commits last year. AI-assisted commits leak at double the base rate.

We built kern to fix this.

What kern is

An open-source TypeScript SDK for managing agent credentials. Built on age encryption. No server required. One dependency.

import { loadIdentityFromHost, openVault } from "kern";

const id    = await loadIdentityFromHost();
const vault = openVault({ identity: id });

const key = await vault.get("openai_api_key");

Secrets are age-encrypted files committed to your repo. Useless without the private key. Auditable in git history. One environment variable (KERN_AGE_KEY) unlocks everything in production.

secrets/
├── .recipients              who can decrypt (age public keys)
├── openai_api_key.age       encrypted, committed to git
├── stripe_spt.age
└── github_token.age

Why not SOPS?

SOPS is the standard for encrypted secrets in git. It's a Go CLI. When you need a secret in your TypeScript app, you shell out to it:

// SOPS
const key = execSync("sops --decrypt --extract '[\"OPENAI_API_KEY\"]' secrets.yaml")
  .toString().trim();
// kern
const key = await vault.get("openai_api_key");

SOPS was built for deploy pipelines — a human encrypts at deploy time, a server decrypts at boot. Kern is built for agents that resolve credentials at runtime. It's an SDK, not a CLI. Import it, call .get(), move on.

It also works client-side. age encryption is pure crypto — no server calls, no network. A browser, a Tauri desktop app, a serverless function can decrypt secrets locally if it has the private key. SOPS can't do this.

Identity

Every kern user gets an age keypair. That's the identity — who is this agent, this machine, this person?

kern identity init      # creates ~/.kern/key
kern identity pubkey    # prints age1...

The public key goes in .recipients. Anyone in the recipients list can decrypt. Add a teammate, add a CI server, add an agent — they all get access by adding their public key.

# add a new team member
echo "age1abc..." >> secrets/.recipients
kern secret rewrap    # re-encrypts everything for the new recipient set

This is the same model as SSH authorized_keys, but for secrets.

The wallet framing

We call kern an "agent wallet" because that's what it is. An agent operating in the world needs:

  • Identity — who am I? (age keypair)
  • API keys — what services can I use? (vault)
  • Payment credentials — can I spend money? (Stripe SPT, x402 wallet key — stored in vault)
  • OAuth tokens — what accounts am I connected to? (vault)

That's a wallet. Not a "secrets manager" — that's the DevOps framing. The agent framing is: every agent needs a keychain, and kern is that keychain.

// agent's wallet holds everything it needs to operate
await vault.put("github_token", "ghp_...");
await vault.put("stripe_spt", paymentToken);
await vault.put("slack_bot_token", "xoxb-...");

// at runtime, agent grabs what it needs
const token = await vault.get("github_token");

Works with Stripe MPP (fiat agent payments), x402 (crypto agent payments), OAuth tokens, API keys — anything an agent needs to operate.

CLI

kern secret add OPENAI_API_KEY    # encrypt and store
kern secret get OPENAI_API_KEY    # decrypt to stdout
kern secret list                  # what's in the vault
kern secret rotate STRIPE_KEY     # replace a value
kern secret rewrap                # re-encrypt for current recipients

We use kern in production. Our CI pipeline has one GitHub secret (KERN_AGE_KEY), and it unlocks everything — R2 credentials, signing keys, API keys, demo account credentials. Fourteen secrets, one key.

What's next

We're building a kern MCP server. Add it to Claude Code:

{
  "mcpServers": {
    "kern": { "command": "npx", "args": ["kern", "mcp"] }
  }
}

Now Claude can resolve credentials from your vault without you pasting API keys into the conversation. The key never enters the LLM's context. The vault stays on your machine.

After that: scoped grants (agent A gives agent B temporary access to specific secrets), an audit ledger (who accessed what, when), and integration with Daslab workspaces for managed agent credential flows.

kern is open source, MIT licensed: github.com/daslabhq/kern


kern uses age by Filippo Valsorda. One dependency: age-encryption on npm. 424 lines of TypeScript.