{
  "markdown": "# Janee 🔐\n\n**Secrets management for AI agents via MCP**\n\n[![npm version](https://img.shields.io/npm/v/@true-and-useful/janee.svg)](https://www.npmjs.com/package/@true-and-useful/janee)\n[![npm downloads](https://img.shields.io/npm/dw/@true-and-useful/janee.svg)](https://www.npmjs.com/package/@true-and-useful/janee)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![GitHub stars](https://img.shields.io/github/stars/rsdouglas/janee.svg?style=social)](https://github.com/rsdouglas/janee)\n\n> Your AI agents need API access to be useful. But they shouldn't have your raw API keys.\n> Janee sits between your agents and your APIs — injecting credentials, enforcing policies, and logging everything.\n\n\n### ✨ Features\n\n| | |\n|---|---|\n| 🔒 **Zero-knowledge agents** | Agents call APIs without ever seeing keys |\n| 📋 **Full audit trail** | Every request logged with timestamp, method, path, status |\n| 🛡️ **Request policies** | Allow/deny rules per capability (e.g., read-only Stripe) |\n| ⏱️ **Session TTLs** | Time-limited access with instant revocation |\n| 🔌 **Works with any MCP client** | Claude Desktop, Cursor, OpenClaw, and more |\n| 🏠 **Local-first** | Keys encrypted on your machine, never sent to a cloud |\n| 🖥️ **Exec mode** | Run CLI tools with injected credentials — agents never see the keys |\n| 🤖 **GitHub App auth** | Short-lived tokens for autonomous agents — no static PATs |\n| 🐦 **Twitter/X OAuth 1.0a** | Per-request OAuth signing — 4 secrets stay encrypted |\n| ☁️ **AWS SigV4** | Sign AWS API requests server-side — SES, S3, and more |\n| 🔧 **Automatic git auth** | `git push/pull` just works when credentials include GitHub tokens |\n\n---\n\n## The Problem\n\nAI agents need API access to be useful. The current approach is to give them your keys and hope they behave.\n\n- 🔓 Agents have full access to Stripe, Gmail, databases\n- 📊 No audit trail of what was accessed or why\n- 🚫 No kill switch when things go wrong\n- 💉 One prompt injection away from disaster\n\n---\n\n## The Solution\n\nJanee is an [MCP](https://modelcontextprotocol.io) server that manages API secrets for AI agents:\n\n1. **Store your API keys** — encrypted locally in `~/.janee/`\n2. **Run `janee serve`** — starts MCP server\n3. **Agent requests access** — via `execute` MCP tool\n4. **Janee injects the real key** — agent never sees it\n5. **Everything is logged** — full audit trail\n\n**Your keys stay on your machine. Agents never see them. You stay in control.**\n\n---\n\n## Configure Once, Use Everywhere\n\nSet up your APIs in Janee once:\n\n```yaml\nservices:\n  stripe:\n    baseUrl: https://api.stripe.com\n    auth: { type: bearer, key: sk_live_xxx }\n  github:\n    baseUrl: https://api.github.com\n    auth: { type: bearer, key: ghp_xxx }\n  openai:\n    baseUrl: https://api.openai.com\n    auth: { type: bearer, key: sk-xxx }\n```\n\nNow **every agent** that connects to Janee can use them:\n\n- **Claude Desktop** — access your APIs\n- **Cursor** — access your APIs  \n- **OpenClaw** — access your APIs\n- **Any MCP client** — access your APIs\n\nNo more copying keys between tools. No more \"which agent has which API configured?\" Add a new agent? It already has access to everything. Revoke a key? Update it once in Janee.\n\n**One config. Every agent. Full audit trail.**\n\n---\n\n## Quick Start\n\n### Install\n\n```bash\nnpm install -g @true-and-useful/janee\n```\n\n### Initialize\n\n```bash\njanee init\n```\n\nThis creates `~/.janee/config.yaml` with example services.\n\n### Add Services\n\n**Option 1: Interactive (recommended for first-time users)**\n\n```bash\njanee add\n```\n\nJanee will guide you through adding a service:\n\n```\nService name: stripe\nBase URL: https://api.stripe.com\nAuth type: bearer\nAPI key: sk_live_xxx\n\n✓ Added service \"stripe\"\n\nCreate a capability for this service? (Y/n): y\nCapability name (default: stripe): \nTTL (e.g., 1h, 30m): 1h\nAuto-approve? (Y/n): y\n\n✓ Added capability \"stripe\"\n\nDone! Run 'janee serve' to start.\n```\n\n**Using an AI agent?** See [Non-interactive Setup](#non-interactive-setup-for-ai-agents) for flags that skip prompts, or the [agent-specific guides](#integrations) below.\n\n**Option 2: Edit config directly**\n\nEdit `~/.janee/config.yaml`:\n\n```yaml\nservices:\n  stripe:\n    baseUrl: https://api.stripe.com\n    auth:\n      type: bearer\n      key: sk_live_xxx\n\ncapabilities:\n  stripe:\n    service: stripe\n    ttl: 1h\n    autoApprove: true\n```\n\n### Add CLI tools (exec mode)\n\nSome tools need credentials as environment variables, not HTTP headers. Exec mode handles this:\n\n```bash\njanee add twitter --exec \\\n  --key \"tvly-xxx\" \\\n  --allow-commands \"bird,tweet-cli\" \\\n  --env-map \"TWITTER_API_KEY={{credential}}\"\n```\n\nNow agents can run CLI tools through Janee without ever seeing the API key:\n\n```typescript\n// Agent calls janee_exec tool\njanee_exec({\n  capability: \"twitter\",\n  command: [\"bird\", \"post\", \"Hello world!\"],\n  cwd: \"/home/agent/project\",  // optional working directory\n  reason: \"User asked to post a tweet\"\n})\n```\n\nJanee spawns the process with `TWITTER_API_KEY` injected, runs the command, and returns stdout/stderr. The credential never enters the agent's context.\n\n**Key flags:**\n- `--exec` — configure as exec-mode (CLI wrapper instead of HTTP proxy)\n- `--allow-commands` — whitelist of allowed executables (security)\n- `--env-map` — map credentials to environment variables\n- `--work-dir` — working directory for the subprocess\n- `--timeout` — max execution time (default: 30s)\n\n\n### Git operations (automatic HTTPS auth)\n\nWhen using exec mode with GitHub credentials, Janee automatically handles git authentication. No extra configuration needed — `git push`, `git pull`, and `git clone` just work:\n\n```yaml\ncapabilities:\n  - name: git-ops\n    service: github\n    mode: exec\n    allowCommands: [git]\n    env:\n      GH_TOKEN: \"{{credential}}\"\n```\n\n```typescript\n// Agent can push code without ever seeing the token\njanee_exec({\n  capability: \"git-ops\",\n  command: [\"git\", \"push\", \"origin\", \"main\"],\n  cwd: \"/workspace/my-repo\"\n})\n```\n\nJanee detects `git` commands with `GH_TOKEN`/`GITHUB_TOKEN` in the environment and creates a temporary askpass script for HTTPS authentication. The script is cleaned up automatically after the command completes.\n\n### Add GitHub App auth (for autonomous agents)\n\nStatic tokens are risky for long-running agents. GitHub App auth generates short-lived installation tokens on demand — no long-lived PATs required.\n\n**Option 1: Use create-gh-app (recommended)**\n\n```bash\nnpx @true-and-useful/create-gh-app create my-agent --owner @me\n# Opens browser → creates app → saves credentials locally\n\n# Install the app on your repos\n# https://github.com/apps/my-agent/installations/new\n\n# Register with Janee in one command\nnpx @true-and-useful/create-gh-app janee-add my-agent\n```\n\nDone. Your agent now gets short-lived GitHub tokens through Janee's MCP proxy.\n\n**Option 2: Manual setup**\n\n```bash\njanee add github-app \\\n  --auth-type github-app \\\n  --app-id 123456 \\\n  --pem-file /path/to/private-key.pem \\\n  --installation-id 789\n```\n\nOr via config:\n\n```yaml\nservices:\n  github:\n    baseUrl: https://api.github.com\n    auth:\n      type: github-app\n      appId: \"123456\"\n      pemFile: /path/to/private-key.pem\n      installationId: \"789\"\n```\n\n**How it works:** When an agent requests access, Janee signs a JWT with the app's private key, exchanges it for a 1-hour installation token via GitHub's API, and caches the token until expiry. The agent never sees the private key — only the short-lived token reaches the API.\n\n### Start the MCP server\n\n```bash\njanee serve\n```\n\n### Use with your agent\n\nAgents that support MCP (Claude Desktop, Cursor, OpenClaw) can now call the `execute` tool to make API requests through Janee:\n\n```typescript\n// Agent calls the execute tool\nexecute({\n  capability: \"stripe\",\n  method: \"GET\",\n  path: \"/v1/balance\",\n  reason: \"User asked for account balance\"\n})\n```\n\nJanee decrypts the key, makes the request, logs everything, and returns the response.\n\n---\n\n## Integrations\n\nWorks with any agent that speaks MCP:\n\n- **OpenClaw** — Native plugin (`@true-and-useful/janee-openclaw`)\n  - **Containerized agents?** See [Container setup guide](docs/container-openclaw.md)\n- **Cursor** — [Setup guide](docs/cursor.md)\n- **Claude Code** — [Setup guide](docs/claude-code.md)\n- **Codex CLI** — [Setup guide](docs/codex.md)\n- **Any MCP client** — just point at `janee serve`\n\n---\n\n## OpenClaw Integration\n\nIf you're using [OpenClaw](https://openclaw.ai), install the plugin for native tool support:\n\n```bash\nnpm install -g @true-and-useful/janee\njanee init\n# Edit ~/.janee/config.yaml with your services\n\n# Install the OpenClaw plugin\nopenclaw plugins install @true-and-useful/janee-openclaw\n```\n\nEnable in your agent config:\n\n```json5\n{\n  agents: {\n    list: [{\n      id: \"main\",\n      tools: { allow: [\"janee\"] }\n    }]\n  }\n}\n```\n\nYour agent now has these tools:\n\n- `janee_list_services` — Discover available APIs\n- `janee_execute` — Make API requests through Janee\n\nThe plugin spawns `janee serve` automatically. All requests are logged to `~/.janee/logs/`.\n\n---\n\n## MCP Tools\n\nJanee exposes three MCP tools:\n\n| Tool | Description |\n|------|-------------|\n| `list_services` | Discover available APIs and their policies |\n| `execute` | Make an API request through Janee (HTTP proxy mode) |\n| `exec` | Run a CLI command with injected credentials (exec mode) |\n| `manage_credential` | View, grant, or revoke access to agent-scoped credentials |\n| `reload_config` | Reload config from disk after adding/removing services (available when started with `janee serve`) |\n\nAgents discover what's available, then call APIs through Janee. Same audit trail, same protection.\n\n---\n\n## Configuration\n\nConfig lives in `~/.janee/config.yaml`:\n\n```yaml\nserver:\n  host: localhost\n\nservices:\n  stripe:\n    baseUrl: https://api.stripe.com\n    auth:\n      type: bearer\n      key: sk_live_xxx  # encrypted at rest\n\n  github:\n    baseUrl: https://api.github.com\n    auth:\n      type: bearer\n      key: ghp_xxx\n\ncapabilities:\n  stripe:\n    service: stripe\n    ttl: 1h\n    autoApprove: true\n\n  stripe_sensitive:\n    service: stripe\n    ttl: 5m\n    requiresReason: true\n```\n\n**Services** = Real APIs with real keys  \n**Capabilities** = What agents can request, with policies\n\n### Supported auth types\n\n| Type | Description | Example |\n|------|-------------|---------|\n| `bearer` | Bearer token in Authorization header | Stripe, OpenAI, GitHub |\n| `basic` | HTTP Basic Auth (username + password) | Internal APIs |\n| `hmac-bybit` | HMAC-SHA256 signing for Bybit | Bybit exchange |\n| `hmac-okx` | HMAC-SHA256 + passphrase for OKX | OKX exchange |\n| `hmac-mexc` | HMAC-SHA256 signing for MEXC | MEXC exchange |\n| `headers` | Custom key-value headers | Non-standard APIs |\n| `service-account` | Google service account JSON key | Google Cloud |\n| `github-app` | Short-lived GitHub installation tokens | GitHub API |\n| `oauth1a-twitter` | OAuth 1.0a per-request signing | Twitter/X API v2 |\n| `aws-sigv4` | AWS Signature V4 per-request signing | SES, S3, and other AWS services |\n\n#### Twitter/X OAuth 1.0a\n\nJanee computes OAuth 1.0a signatures (HMAC-SHA1) server-side, so your 4 Twitter secrets stay encrypted at rest and never enter the agent's context:\n\n```yaml\nservices:\n  twitter:\n    baseUrl: https://api.x.com\n    auth:\n      type: oauth1a-twitter\n      consumerKey: xxx        # encrypted at rest\n      consumerSecret: xxx     # encrypted at rest\n      accessToken: xxx        # encrypted at rest\n      accessTokenSecret: xxx  # encrypted at rest\n\ncapabilities:\n  twitter:\n    service: twitter\n    ttl: 1h\n    autoApprove: true\n```\n\nOr use the built-in template:\n\n```bash\njanee add twitter\n```\n\n#### AWS SigV4\n\nJanee computes AWS Signature V4 (HMAC-SHA256) per-request, keeping your access keys encrypted at rest. Non-secret fields (`region`, `awsService`) stay in plain config:\n\n```yaml\nservices:\n  aws-ses:\n    baseUrl: https://email.us-east-1.amazonaws.com\n    auth:\n      type: aws-sigv4\n      accessKeyId: AKIA...     # encrypted at rest\n      secretAccessKey: xxx     # encrypted at rest\n      region: us-east-1\n      awsService: ses\n\ncapabilities:\n  aws-ses:\n    service: aws-ses\n    ttl: 1h\n    autoApprove: true\n```\n\nBuilt-in templates for common AWS services:\n\n```bash\njanee add aws-ses    # Amazon SES\njanee add aws-s3     # Amazon S3\n```\n\n### Access control\n\nControl which agents can use which capabilities:\n\n```yaml\nserver:\n  host: localhost\n  defaultAccess: restricted   # capabilities require explicit allowlist\n\ncapabilities:\n  stripe:\n    service: stripe\n    ttl: 1h\n    allowedAgents: [\"agent-a\", \"agent-b\"]   # only these agents can use it\n\n  github:\n    service: github\n    ttl: 1h\n    # no allowedAgents + defaultAccess: restricted → no agent can use this\n```\n\n- **`defaultAccess: restricted`** — capabilities without an `allowedAgents` list are hidden from all agents\n- **`defaultAccess: open`** (default) — capabilities without an `allowedAgents` list are available to all agents\n- **`allowedAgents`** — per-capability list of agent names (matched against `clientInfo.name` from the MCP initialize handshake)\n\nCredentials created by agents at runtime default to `agent-only` access — only the creating agent can use them unless it explicitly grants access via the `manage_credential` tool.\n\n### Exec mode capabilities\n\n```yaml\nservices:\n  twitter:\n    auth:\n      type: bearer\n      key: tvly-xxx\n\ncapabilities:\n  twitter:\n    service: twitter\n    mode: exec\n    allowCommands: [\"bird\", \"tweet-cli\"]\n    envMap:\n      TWITTER_API_KEY: \"{{credential}}\"\n    ttl: 1h\n    autoApprove: true\n```\n\nExec-mode capabilities use `janee_exec` instead of `execute`. The credential is injected as an environment variable — the agent sees only stdout/stderr.\n\nRunner hardening defaults in exec mode:\n- isolated minimal environment (no full host env inheritance)\n- temporary `HOME` per command\n- timeout kills the process group\n\n### Runner/Authority mode (for containers)\n\nWhen agents run inside Docker containers, `janee_exec` on a remote host cannot access the container filesystem. The Runner/Authority architecture solves this:\n\n- **Authority** runs on the host: holds credentials, enforces policy, proxies API requests\n- **Runner** runs inside each container: serves MCP to the agent, forwards non-exec calls to the Authority, runs `janee_exec` locally\n\n```bash\n# Host: start Authority (MCP + exec authorization on one port)\njanee serve -t http -p 3100 --host 0.0.0.0 --runner-key \"$JANEE_RUNNER_KEY\"\n\n# Container: start Runner (agent talks to this)\njanee serve -t http -p 3200 --host 127.0.0.1 \\\n  --authority http://host.docker.internal:3100 --runner-key \"$JANEE_RUNNER_KEY\"\n```\n\nThe agent only needs `JANEE_URL=http://localhost:3200`.\n\nYou can also run the Authority as a standalone process:\n\n```bash\njanee authority --runner-key \"$JANEE_RUNNER_KEY\" --host 127.0.0.1 --port 9120\n```\n\nSee the [Runner/Authority guide](docs/runner-authority.md) for the full architecture, exec authorization flow, Docker Compose example, and troubleshooting.\n\n\n---\n\n## Request Policies\n\nControl exactly what requests each capability can make using `rules`:\n\n```yaml\ncapabilities:\n  stripe_readonly:\n    service: stripe\n    ttl: 1h\n    rules:\n      allow:\n        - GET *\n      deny:\n        - POST *\n        - PUT *\n        - DELETE *\n\n  stripe_billing:\n    service: stripe\n    ttl: 15m\n    requiresReason: true\n    rules:\n      allow:\n        - GET *\n        - POST /v1/refunds/*\n        - POST /v1/invoices/*\n      deny:\n        - POST /v1/charges/*  # Can't charge cards\n        - DELETE *\n```\n\n**How rules work:**\n\n1. **`deny` patterns are checked first** — explicit deny always wins\n2. **Then `allow` patterns are checked** — must match to proceed\n3. **No rules defined** → allow all (backward compatible)\n4. **Rules defined but no match** → denied by default\n\n**Pattern format:** `METHOD PATH`\n\n- `GET *` → any GET request\n- `POST /v1/charges/*` → POST to /v1/charges/ and subpaths\n- `* /v1/customers` → any method to /v1/customers\n- `DELETE /v1/customers/*` → DELETE any customer\n\n**This makes security real:** Even if an agent lies about its \"reason\", it can only access the endpoints the policy allows. Enforcement happens server-side.\n\n---\n\n## CLI Reference\n\n```bash\njanee init                    # Set up ~/.janee/ with example config\njanee add                     # Add a service (interactive)\njanee add stripe -u https://api.stripe.com -k sk_xxx  # Add with args\njanee remove <service>        # Remove a service\njanee remove <service> --yes  # Remove without confirmation\njanee list                    # List configured services\njanee list --json             # Output as JSON (for integrations)\njanee search [query]          # Search service directory\njanee search stripe --json    # Search with JSON output\njanee cap list                # List capabilities\njanee cap list --json         # List capabilities as JSON\njanee cap add <name> --service <service>  # Add capability\njanee cap edit <name>         # Edit capability\njanee cap remove <name>       # Remove capability\njanee serve                   # Start MCP server (stdio, default)\njanee serve --transport http --port 9100  # Start with HTTP transport (for containers)\njanee serve --authority https://janee.example.com --runner-key $JANEE_RUNNER_KEY  # Runner mode\njanee authority --runner-key $JANEE_RUNNER_KEY  # Start authority API\njanee logs                    # View audit log\njanee logs -f                 # Tail audit log\njanee logs --json             # Output as JSON\njanee sessions                # List active sessions\njanee sessions --json         # Output as JSON\njanee revoke <id>             # Kill a session\n```\n\n### Non-interactive Setup (for AI agents)\n\nAI agents can't respond to interactive prompts. Use `--*-from-env` flags to read credentials from environment variables — this keeps secrets out of the agent's context window:\n\n```bash\n# Bearer auth (Stripe, OpenAI, etc.)\njanee add stripe -u https://api.stripe.com --auth-type bearer --key-from-env STRIPE_KEY\n\n# HMAC auth (Bybit)\njanee add bybit --auth-type hmac-bybit --key-from-env BYBIT_KEY --secret-from-env BYBIT_SECRET\n\n# HMAC auth with passphrase (OKX)\njanee add okx --auth-type hmac-okx --key-from-env OKX_KEY --secret-from-env OKX_SECRET --passphrase-from-env OKX_PASS\n\n# GitHub App auth (short-lived tokens)\njanee add github --auth-type github-app --app-id-from-env GH_APP_ID --pem-from-env GH_PEM --installation-id-from-env GH_INSTALL_ID\n\n# Twitter/X OAuth 1.0a (per-request signing)\njanee add twitter --consumer-key $TWITTER_CONSUMER_KEY --consumer-secret $TWITTER_CONSUMER_SECRET \\\n  --access-token $TWITTER_ACCESS_TOKEN --access-token-secret $TWITTER_ACCESS_TOKEN_SECRET\n\n# AWS SigV4 (SES, S3, etc.)\njanee add aws-ses --access-key-id $AWS_ACCESS_KEY_ID --secret-access-key $AWS_SECRET_ACCESS_KEY \\\n  --region us-east-1 --aws-service ses\n```\n\nWhen all required credentials are provided via flags, Janee:\n- Never opens readline (no hanging on stdin)\n- Auto-creates a capability with sensible defaults (1h TTL, auto-approve)\n\nYou can also edit `~/.janee/config.yaml` directly if you prefer.\n\n---\n\n## How It Works\n\n```\n┌─────────────┐      ┌──────────┐      ┌─────────┐\n│  AI Agent   │─────▶│  Janee   │─────▶│  Stripe │\n│             │ MCP  │   MCP    │ HTTP │   API   │\n└─────────────┘      └──────────┘      └─────────┘\n      │                   │\n   No key           Injects key\n                    + logs request\n```\n\n1. Agent calls `execute` MCP tool with capability, method, path\n2. Janee looks up service config, decrypts the real key\n3. Makes HTTP request to real API with key\n4. Logs: timestamp, service, method, path, status\n5. Returns response to agent\n\nAgent never touches the real key.\n\n> 📐 **Deep dive:** See [Architecture & Security Model](docs/architecture.md) for detailed diagrams, threat model, and comparison with alternatives.\n\n---\n\n## Security\n\n- **Encryption**: Keys stored with AES-256-GCM\n- **Agent identity**: Derived from `clientInfo.name` in the MCP initialize handshake — no custom headers needed\n- **Agent isolation**: Each agent gets its own session with isolated identity (HTTP transport creates a Server+Transport per session)\n- **Access control**: Per-capability `allowedAgents` whitelist + server-wide `defaultAccess` policy\n- **Credential scoping**: Agent-created credentials default to `agent-only`\n- **Audit log**: Every request logged to `~/.janee/logs/`\n- **Sessions**: Time-limited, revocable\n- **Kill switch**: `janee revoke` or delete config\n\n---\n\n\n## Docker\n\nRun Janee as a container — no local Node.js required:\n\n```bash\n# Build\ndocker build -t janee .\n\n# Run in HTTP mode\ndocker run -d -p 3000:3000 \\\n  -v ~/.janee:/root/.janee:ro \\\n  janee --transport http --port 3000 --host 0.0.0.0\n```\n\nOr use Docker Compose:\n\n```bash\nmkdir -p config && cp ~/.janee/config.yaml config/\ndocker compose up -d\n```\n\nFor Claude Desktop with Docker, see [Docker docs](docs/docker.md).\n\n---\n## Contributing\n\nWe welcome contributions! Please read **[CONTRIBUTING.md](docs/CONTRIBUTING.md)** before submitting a PR — it includes the required PR checklist (tests, changelog, version bump, etc.).\n\n---\n\n## License\n\nMIT — Built by [True and Useful LLC](https://trueanduseful.com)\n\n---\n\n**Stop giving AI agents your keys. Start controlling access.** 🔐\n",
  "bytes": 21314,
  "sha": "685a2613e0fc9271c1cf1539b11bed154faafb940dbf992c0191b7a61935dfaf",
  "repo_slug": "rsdouglas/janee",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_rsdouglas_janee_28dd6d1c/readme"
}