{"_id":"@daslabhq/kern","name":"@daslabhq/kern","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@daslabhq/kern","version":"0.2.0","description":"The agent wallet. Hold credentials — agents use them without seeing them.","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./vault":{"types":"./dist/vault.d.ts","default":"./dist/vault.js"},"./wallet":{"types":"./dist/wallet.d.ts","default":"./dist/wallet.js"},"./identity":{"types":"./dist/identity.d.ts","default":"./dist/identity.js"},"./env":{"types":"./dist/env.d.ts","default":"./dist/env.js"},"./serve":{"types":"./dist/serve.d.ts","default":"./dist/serve.js"},"./mcp":{"types":"./dist/mcp.d.ts","default":"./dist/mcp.js"}},"bin":{"kern":"bin/kern.ts"},"keywords":["kern","agent","wallet","secrets","age","vault","identity","encryption","mcp","proxy","credentials"],"license":"MIT","dependencies":{"age-encryption":"^0.3.0"},"devDependencies":{"bun-types":"^1.3.14","typescript":"^5.4.0"},"scripts":{"build":"tsc","prepublishOnly":"npm run build","test":"bun test ./test/smoke.ts ./test/wallet.test.ts ./test/serve.test.ts"},"_id":"@daslabhq/kern@0.2.0","gitHead":"062fd0f624a722018cc407c00923c24b5046ab22","_nodeVersion":"22.17.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-pYoL2rY5gitMibe1gmEmpAJ8rLzB5Heoq7EUXbwozVEJlNTjqKwoePDy8e8FCE61CjXRIEoXRC+K/WsLa4frew==","shasum":"dfdb83735c775cd1b76cc3b371d573b625b87a03","tarball":"https://registry.npmjs.org/@daslabhq/kern/-/kern-0.2.0.tgz","fileCount":25,"unpackedSize":78291,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCsTCWMAxU0vD1x1RLV/9OlqbcBbdmOdL3NZEesqSP3zAIgatRe5ua1JHO/BOrY4ydfDh1kJYG6fw3r3odsh7HR6tI="}]},"_npmUser":{"name":"mirkok","email":"mail@mirkokiefer.com"},"directories":{},"maintainers":[{"name":"mirkok","email":"mail@mirkokiefer.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/kern_0.2.0_1779908251012_0.7180504148232569"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-27T18:57:30.886Z","0.2.0":"2026-05-27T18:57:31.169Z","modified":"2026-05-27T18:57:31.363Z"},"maintainers":[{"name":"mirkok","email":"mail@mirkokiefer.com"}],"description":"The agent wallet. Hold credentials — agents use them without seeing them.","keywords":["kern","agent","wallet","secrets","age","vault","identity","encryption","mcp","proxy","credentials"],"license":"MIT","readme":"# kern\n\n[![tests](https://github.com/daslabhq/kern/actions/workflows/test.yml/badge.svg)](https://github.com/daslabhq/kern/actions/workflows/test.yml)\n\nThe agent wallet. Hold credentials — agents use them without seeing them.\n\n```bash\nnpm install @daslabhq/kern\n```\n\nWorks with Node.js 18+ and [Bun](https://bun.sh). CLI requires Bun.\n\n## The problem\n\nAgents need API keys. Today those live in env vars — plaintext, unscoped, every process sees everything. The agent calling Stripe has your production secret key in its context window. Every credential is one prompt injection away from exfiltration.\n\n## How kern works\n\nKern is a credential wallet backed by [age encryption](https://age-encryption.org/). Credentials are encrypted files, organized in folders, committed to git. The wallet holds them. Agents use them two ways:\n\n**Proxy** — the credential never leaves the wallet. The agent asks kern to make the API call; kern injects the auth and returns the response.\n\n```typescript\nimport { openWallet, loadIdentityFromHost } from \"@daslabhq/kern\";\n\nconst wallet = openWallet({ identity: await loadIdentityFromHost() });\n\n// wallet injects the Bearer token — agent never sees it\nconst resp = await wallet.fetch(\"tokens/github\", \"https://api.github.com/user/repos\");\nconst repos = await resp.json();\n```\n\n**Direct** — for SDKs and non-HTTP protocols where you need the raw credential.\n\n```typescript\nconst key = await wallet.get(\"tokens/openai\");\nconst client = new OpenAI({ apiKey: key });\n```\n\nBoth read from the same encrypted vault. You choose per credential.\n\n## Quick start\n\n```bash\n# create your identity (age keypair)\nkern identity init\n\n# create the vault\nmkdir -p secrets\nkern identity pubkey >> secrets/.recipients\n\n# add credentials\nkern secret add tokens/github\nkern secret add tokens/openai\n\n# proxy request — credential stays in the wallet\nkern fetch tokens/github https://api.github.com/user\n```\n\n## Agent integration (MCP)\n\nAdd kern as an MCP server. The agent talks to the wallet — never holds the keys.\n\n```json\n{\n  \"mcpServers\": {\n    \"kern\": {\n      \"command\": \"npx\",\n      \"args\": [\"@daslabhq/kern\", \"mcp\"]\n    }\n  }\n}\n```\n\n```\nAgent: \"Fetch my GitHub repos\"\n\n→ kern_fetch(secret: \"tokens/github\", url: \"https://api.github.com/user/repos\")\n→ wallet decrypts tokens/github, injects Bearer token, makes the request\n→ returns JSON response to agent\n\nAgent got the data. Never saw the token.\n```\n\n```\nAgent: \"Add my Stripe test key\"\n\n→ kern_add(name: \"testing/stripe\")\n→ browser opens kern's local form\n→ you paste the key\n→ encrypted into secrets/testing/stripe.age\n\nCredential went: browser → wallet → encrypted file. Agent never saw it.\n```\n\n### MCP tools\n\n| Tool | Mode | Description |\n|------|------|-------------|\n| `kern_fetch` | proxy | Authenticated HTTP request. Credential stays in the wallet. |\n| `kern_get` | direct | Decrypt and return a credential value. |\n| `kern_add` | — | Add a credential via browser form. Agent never sees it. |\n| `kern_rotate` | — | Replace a credential via browser form. |\n| `kern_remove` | — | Delete a credential. |\n| `kern_list` | — | List credential names (never values). |\n| `kern_status` | — | Wallet health check. |\n| `kern_recipients` | — | List recipients (public keys). |\n\n## Vault layout\n\nCredentials are age-encrypted files in folders. Each folder has a `.recipients` file controlling who can decrypt. Commit the whole thing to git — it's ciphertext without the private key.\n\n```\nsecrets/\n├── .recipients              # all nodes\n│\n├── tokens/                  # API credentials\n│   ├── github.age\n│   ├── openai.age\n│   └── stripe.age\n│\n├── infra/                   # production infrastructure\n│   ├── database_url.age\n│   └── redis_url.age\n│\n└── testing/                 # dev + CI only\n    ├── .recipients          # narrower: Alice + CI (not prod)\n    ├── stripe_test.age\n    └── llm.age\n```\n\n### Scoping rules\n\n- Each folder can have its own `.recipients`\n- No `.recipients`? Inherits from parent\n- Prod server decrypts `tokens/` and `infra/` — never sees test keys\n- CI decrypts `testing/` — never touches prod credentials\n\n## Nodes\n\nA node is any machine with an age keypair — your laptop, CI, a production server.\n\n### Add a teammate\n\n```bash\n# Bob generates his identity\nkern identity init && kern identity pubkey\n# → age1xyz...\n\necho \"age1xyz...\" >> secrets/.recipients\necho \"age1xyz...\" >> secrets/testing/.recipients\nkern secret rewrap\ngit commit -am \"add Bob\"\n```\n\n### Add CI\n\n```bash\nkern identity init --save /tmp/ci-key\necho \"$(kern identity pubkey)\" >> secrets/.recipients\necho \"$(kern identity pubkey)\" >> secrets/testing/.recipients\nkern secret rewrap\ngh secret set KERN_AGE_KEY < /tmp/ci-key\nrm /tmp/ci-key\n```\n\nOne secret in CI. Everything else decrypts from git.\n\n### Revoke access\n\n```bash\nkern recipients remove age1xyz...\nkern secret rewrap\nkern secret rotate tokens/github  # rotate what they had access to\n```\n\n## Machine payments\n\nThe same proxy that protects API keys protects payment credentials. An agent that purchases compute, calls paid APIs, or manages subscriptions needs payment keys — but shouldn't hold them.\n\n```typescript\n// agent charges a customer — never sees sk_live_*\nconst resp = await wallet.fetch(\"tokens/stripe\", \"https://api.stripe.com/v1/payment_intents\", {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n    body: \"amount=2000&currency=usd&automatic_payment_methods[enabled]=true\",\n});\n```\n\nWorks with [Stripe MPP](https://docs.stripe.com/payments/machine/mpp) for machine-to-machine payments and any API that takes Bearer auth. [x402](https://www.x402.org/) support — auto-negotiating `402 Payment Required` responses — is on the roadmap.\n\n## CLI\n\n```bash\nkern identity init [--save PATH]   # create age keypair\nkern identity pubkey               # print public key\n\nkern secret add [FOLDER/]NAME      # encrypt and store\nkern secret get [FOLDER/]NAME      # decrypt to stdout\nkern secret list                   # show all names\nkern secret rotate [FOLDER/]NAME   # replace a value\nkern secret delete [FOLDER/]NAME   # remove\nkern secret rewrap                 # re-encrypt for current recipients\n\nkern fetch SECRET URL [OPTIONS]    # proxy request (credential stays in wallet)\n  --method POST                    # HTTP method (default GET)\n  --body '{\"key\": \"val\"}'          # request body\n\nkern recipients                    # list all recipients\nkern recipients remove KEY         # remove from all folders\n\nkern mcp                           # start MCP server\nkern serve                         # start local credential form\n```\n\n## Environment\n\n| Variable | Purpose | Default |\n|---|---|---|\n| `KERN_AGE_KEY` | age private key | `~/.kern/key` |\n| `KERN_VAULT_DIR` | vault directory | `./secrets` |\n\n## How it compares\n\n| | .env | SOPS | Vault | kern |\n|---|---|---|---|---|\n| Encrypted at rest | | ✓ | ✓ | ✓ |\n| Lives in git | | ✓ | | ✓ |\n| Folder scoping | | | ✓ | ✓ |\n| No server | ✓ | ✓ | | ✓ |\n| Proxy mode | | | | ✓ |\n| Agent-native (MCP) | | | | ✓ |\n| TypeScript SDK | | | | ✓ |\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-3121c3e14ac905d812c844edea7d0501"}