{"id":45040984,"url":"https://github.com/joelhooks/agent-secrets","last_synced_at":"2026-04-01T19:02:59.295Z","repository":{"id":336063814,"uuid":"1147725667","full_name":"joelhooks/agent-secrets","owner":"joelhooks","description":"🛡️ Portable credential management for AI agents — Age encryption, session leases, killswitch","archived":false,"fork":false,"pushed_at":"2026-03-20T01:17:45.000Z","size":6026,"stargazers_count":65,"open_issues_count":15,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-20T17:16:03.650Z","etag":null,"topics":["age-encryption","ai-agents","cli","encryption","go","secrets-management","security"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joelhooks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-02T06:08:21.000Z","updated_at":"2026-03-20T06:02:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/joelhooks/agent-secrets","commit_stats":null,"previous_names":["joelhooks/agent-secrets"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/joelhooks/agent-secrets","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Fagent-secrets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Fagent-secrets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Fagent-secrets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Fagent-secrets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joelhooks","download_url":"https://codeload.github.com/joelhooks/agent-secrets/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joelhooks%2Fagent-secrets/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291016,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["age-encryption","ai-agents","cli","encryption","go","secrets-management","security"],"created_at":"2026-02-19T07:26:08.753Z","updated_at":"2026-04-01T19:02:59.277Z","avatar_url":"https://github.com/joelhooks.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🛡️ agent-secrets\n\n**Aegis for AI Agents** — Portable credential management with age encryption, session-scoped leases, and a killswitch.\n\n[![Go](https://img.shields.io/badge/Go-1.22+-00ADD8?style=flat\u0026logo=go)](https://golang.org)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n## Why?\n\nAI agents need secrets: API keys, tokens, credentials. But giving an agent raw access to your password manager (and therefore all your passwords) is a terrible idea:\n\n- **Exfiltration risk** — compromised agent leaks all credentials\n- **No audit trail** — who accessed what, when?\n- **No revocation** — can't cut off access without rotating everything\n- **No rotation** — credentials sit forever, waiting to be compromised\n\n**agent-secrets** fixes this with:\n\n- 🔐 **Age encryption** — secrets never plaintext on disk\n- ⏱️ **Session-scoped leases** — TTL-based access, auto-expires\n- 🔄 **Auto-rotation hooks** — `gh auth refresh`, custom commands\n- 🚨 **Multi-factor killswitch** — revoke all + rotate all + wipe\n- 📋 **Append-only audit log** — every access recorded\n\n## Installation\n\n**One-liner (macOS, Linux, WSL):**\n```bash\ncurl -fsSL https://raw.githubusercontent.com/joelhooks/agent-secrets/main/install.sh | bash\n```\n\n**Or clone and install manually:**\n```bash\ngit clone https://github.com/joelhooks/agent-secrets\ncd agent-secrets\nmake build\nsudo mv secrets /usr/local/bin/\n```\n\n**Or with Go:**\n```bash\ngo install github.com/joelhooks/agent-secrets/cmd/secrets@latest\n```\n\n**Add the OpenClaw skill (teaches your agent how to use it):**\n```bash\nnpx openclaw skills add https://github.com/joelhooks/agent-secrets\n```\n\n**Verify (self-documenting root command):**\n```bash\nsecrets\n```\n\n## Quick Start\n\n```bash\n# 1) Initialize store and start daemon (one-time setup)\nsecrets init\nsecrets serve \u0026\n\n# 2) Add secrets\nsecrets add github_token --rotate-via \"gh auth refresh\"\nsecrets add anthropic_key\necho \"sk-ant-...\" | secrets add openai_key\n\n# 3) Discover commands and available secrets\nsecrets\nsecrets list\n\n# 4) Lease a secret (default output is raw value, ideal for exports)\nexport GITHUB_TOKEN=$(secrets lease github_token)\n\n# 5) Request JSON envelope when you need metadata + next actions\nsecrets lease github_token --json\n\n# 6) Inspect state and logs\nsecrets status\nsecrets audit --tail 20\n\n# 7) Emergency killswitch\nsecrets revoke --all\n```\n\n\u003e **Note:** The daemon must be running for most commands to work. The install script auto-starts it, but if you see \"daemon not running\" errors, run `secrets serve \u0026`.\n\n## Migration Guide: v0.4.x -\u003e v0.5.x\n\n`v0.5.x` keeps compatibility with older scripts while moving to JSON-first behavior.\n\n- `secrets lease \u003cname\u003e` now defaults to raw value output. `--raw` still works as a hidden deprecated no-op and prints a warning to `stderr`. Remove it from scripts before `v0.6.0`.\n- `--human` and `--output` are restored as hidden deprecated global flags. They are no-ops and print warnings to `stderr`. Remove them before `v0.6.0`.\n- JSON envelopes now include both `ok` and `success` for one version cycle. They carry the same boolean value.\n\n## JSON Envelope Pattern\n\nAll commands return a JSON response envelope with `ok`, `success`, `command`, `result`, and `next_actions`, except `secrets lease \u003cname\u003e` which returns only the raw secret value by default.\n\n```bash\n# Root command returns command tree\nsecrets\n```\n\n```json\n{\n  \"ok\": true,\n  \"command\": \"secrets\",\n  \"result\": {\n    \"description\": \"Portable credential management for AI agents\",\n    \"version\": \"dev\",\n    \"commands\": [\n      {\"name\": \"init\", \"description\": \"Initialize the secrets store\", \"usage\": \"secrets init\"},\n      {\"name\": \"list\", \"description\": \"List stored secret names\", \"usage\": \"secrets list\"},\n      {\"name\": \"lease\", \"description\": \"Get a secret value (raw by default)\", \"usage\": \"secrets lease \u003cname\u003e [--ttl 1h] [--client-id agent-x] [--json]\"}\n    ]\n  },\n  \"next_actions\": [\n    {\"command\": \"secrets status\", \"description\": \"Check daemon status\"}\n  ]\n}\n```\n\n```bash\n# Lease envelope (opt in)\nsecrets lease github_token --json\n```\n\n```json\n{\n  \"ok\": true,\n  \"command\": \"secrets lease\",\n  \"result\": {\n    \"lease_id\": \"lease-123\",\n    \"secret_name\": \"github_token\",\n    \"value\": \"ghp_xxx\",\n    \"expires_at\": \"2026-02-19T12:00:00Z\",\n    \"ttl\": \"1h\",\n    \"client_id\": \"my-agent\"\n  },\n  \"next_actions\": [\n    {\"command\": \"export GITHUB_TOKEN=$(secrets lease github_token)\", \"description\": \"Export to environment\"},\n    {\"command\": \"secrets revoke lease-123\", \"description\": \"Revoke this lease\"}\n  ]\n}\n```\n\n```json\n{\n  \"ok\": false,\n  \"command\": \"secrets lease\",\n  \"error\": {\n    \"message\": \"failed to acquire lease: ... secret not found\",\n    \"code\": \"generic_error\"\n  },\n  \"fix\": \"Check available secrets: secrets status\"\n}\n```\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                         CLI (cobra)                         │\n│   init | add | lease | revoke | audit | status              │\n└─────────────────────────┬───────────────────────────────────┘\n                          │ Unix Socket (JSON-RPC)\n┌─────────────────────────▼───────────────────────────────────┐\n│                    Daemon Process                           │\n│         ~/.agent-secrets/agent-secrets.sock                 │\n└─────┬─────────┬─────────┬─────────┬─────────┬───────────────┘\n      │         │         │         │         │\n      ▼         ▼         ▼         ▼         ▼\n┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐\n│  Store  │ │  Lease  │ │  Audit  │ │Rotation │ │ Killswitch  │\n│  (age)  │ │ Manager │ │   Log   │ │  Hooks  │ │ +Heartbeat  │\n└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────────┘\n```\n\n## Commands\n\n### `secrets`\nShow a self-documenting command tree for agent discovery.\n\n```bash\nsecrets\n```\n\n### `secrets init`\nInitialize the encrypted store. Creates:\n- `~/.agent-secrets/identity.age` — X25519 private key\n- `~/.agent-secrets/secrets.age` — encrypted secrets\n- `~/.agent-secrets/config.json` — configuration\n\n### `secrets add \u003cname\u003e`\nAdd a secret to the store.\n\n```bash\n# Interactive (prompts for value)\nsecrets add my_secret\n\n# With rotation hook\nsecrets add github_token --rotate-via \"gh auth refresh\"\n\n# Pipe value from stdin\necho \"secret-value\" | secrets add api_key\ncat credentials.txt | secrets add service_account\n```\n\n### `secrets list`\nList stored secret names and lease-related metadata for discovery.\n\n```bash\nsecrets list\n```\n\n### `secrets lease \u003cname\u003e`\nAcquire a time-bounded lease on a secret.\n\n- Default output: **raw secret value only** (best for shell exports)\n- `--json`: full envelope with lease metadata and `next_actions`\n\n```bash\n# Primary pattern: raw value for shell export\nexport TOKEN=$(secrets lease github_token)\n\n# Custom TTL\nexport TOKEN=$(secrets lease github_token --ttl 30m)\n\n# Custom client ID (for audit)\nexport TOKEN=$(secrets lease github_token --client-id \"my-agent\")\n\n# JSON envelope for agents\nsecrets lease github_token --json\n```\n\n### `secrets revoke [lease-id]`\nRevoke access.\n\n```bash\n# Revoke specific lease\nsecrets revoke abc123\n\n# KILLSWITCH: Revoke ALL active leases\nsecrets revoke --all\n```\n\n### `secrets audit`\nView the append-only audit log.\n\n```bash\n# Last 50 entries (default)\nsecrets audit\n\n# Last 100 entries\nsecrets audit --tail 100\n```\n\n### `secrets status`\nShow daemon status.\n\n```bash\nsecrets status\n```\n\n```json\n{\n  \"ok\": true,\n  \"command\": \"secrets status\",\n  \"result\": {\n    \"running\": true,\n    \"secrets_count\": 5,\n    \"active_leases\": 2,\n    \"started_at\": \"2026-02-19T10:30:00Z\",\n    \"uptime\": \"1h 15m\"\n  },\n  \"next_actions\": [\n    {\"command\": \"secrets lease \u003cname\u003e\", \"description\": \"Get a secret value\"}\n  ]\n}\n```\n\n### `secrets env`\nGenerate `.env` file from `.secrets.json` config. Perfect for agentic workflows where secrets need to be loaded into a project environment.\n\n```bash\n# Generate .env from .secrets.json in current directory\nsecrets env\n\n# Force overwrite existing .env\nsecrets env --force\n```\n\n**How it works:**\n1. Reads `.secrets.json` from current directory\n2. Acquires leases for each secret listed\n3. Writes `KEY=value` pairs to `.env` file\n4. Sets restrictive permissions (0600)\n\n**Example `.secrets.json`:**\n```json\n{\n  \"secrets\": [\n    {\n      \"name\": \"github_token\",\n      \"env_var\": \"GITHUB_TOKEN\"\n    },\n    {\n      \"name\": \"anthropic_key\",\n      \"env_var\": \"ANTHROPIC_API_KEY\"\n    },\n    {\n      \"name\": \"openai_key\",\n      \"env_var\": \"OPENAI_API_KEY\",\n      \"ttl\": \"30m\"\n    }\n  ],\n  \"client_id\": \"my-project\"\n}\n```\n\n**Schema:**\n- `secrets` (required): Array of secret mappings\n  - `name` (required): Secret name in agent-secrets store\n  - `env_var` (required): Environment variable name for .env file\n  - `ttl` (optional): Custom TTL for this secret (default: 1h)\n- `client_id` (optional): Custom client ID for audit trail (default: auto-generated)\n\n### `secrets exec`\nRun a command with secrets loaded as environment variables. Combines `secrets env` + command execution + automatic cleanup.\n\n```bash\n# Run command with secrets loaded\nsecrets exec -- npm run dev\n\n# Run tests with credentials\nsecrets exec -- pytest tests/\n\n# Execute shell script\nsecrets exec -- ./deploy.sh\n\n# Chain multiple commands\nsecrets exec -- sh -c \"npm install \u0026\u0026 npm test\"\n```\n\n**What it does:**\n1. Generates temporary `.env` file from `.secrets.json`\n2. Executes command with environment loaded\n3. Cleans up `.env` file when command exits (even on error)\n\n### `secrets cleanup`\nRemove expired lease environment files. Run this to clean up stale `.env` files when leases have expired.\n\n```bash\n# Remove all expired .env files\nsecrets cleanup\n\n# Check what would be cleaned (dry-run)\nsecrets cleanup --dry-run\n```\n\n## Security Model\n\n### Encryption\n- All secrets encrypted at rest using [age](https://age-encryption.org/)\n- X25519 key pair generated on init\n- Identity file permissions: `0600`\n\n### Leases\n- Secrets **cannot** be accessed directly — must acquire a lease\n- Leases have mandatory TTL (max 24h by default)\n- Expired leases automatically cleaned up\n- Background goroutine prunes expired leases\n\n### Audit\n- Every operation logged with timestamp\n- Append-only format (JSONL)\n- Logs: secret access, lease grants, revocations, rotations, killswitch events\n\n### Killswitch\n- `revoke --all` immediately invalidates all active leases\n- Optional: rotate all secrets with hooks\n- Optional: wipe entire store\n- Optional: heartbeat monitor — auto-killswitch if remote endpoint goes down\n\n## Configuration\n\nConfig stored at `~/.agent-secrets/config.json`:\n\n```json\n{\n  \"directory\": \"/home/user/.agent-secrets\",\n  \"socket_path\": \"/home/user/.agent-secrets/agent-secrets.sock\",\n  \"default_lease_ttl\": \"1h\",\n  \"max_lease_ttl\": \"24h\",\n  \"rotation_timeout\": \"30s\",\n  \"heartbeat\": {\n    \"enabled\": false,\n    \"url\": \"https://your-endpoint.com/heartbeat\",\n    \"interval\": \"1m\",\n    \"timeout\": \"10s\",\n    \"fail_action\": {\n      \"revoke_all\": true,\n      \"rotate_all\": false,\n      \"wipe_store\": false\n    }\n  }\n}\n```\n\n## Agent Integration\n\nOnce the CLI is installed globally (`secrets` in PATH), any AI agent with shell access can use it directly. For richer integration, install the skill documentation or platform plugins.\n\n### Any Agent (Direct CLI)\n\nWorks out of the box with any agent that can run shell commands.\n\n**Option 1: Direct lease (single secret)**\n```bash\n# Lease credentials for a task\nexport GITHUB_TOKEN=$(secrets lease github_token --ttl 1h --client-id \"claude-refactor-123\")\nexport ANTHROPIC_API_KEY=$(secrets lease anthropic_key --ttl 1h --client-id \"claude-refactor-123\")\n\n# Check status\nsecrets status\n\n# Revoke when done\nsecrets revoke --all\n```\n\n**Option 2: Project-based workflow (.secrets.json)**\n```bash\n# 1. Create .secrets.json in project root\ncat \u003e .secrets.json \u003c\u003c'EOF'\n{\n  \"secrets\": [\n    {\"name\": \"github_token\", \"env_var\": \"GITHUB_TOKEN\"},\n    {\"name\": \"anthropic_key\", \"env_var\": \"ANTHROPIC_API_KEY\"},\n    {\"name\": \"vercel_token\", \"env_var\": \"VERCEL_TOKEN\", \"ttl\": \"30m\"}\n  ],\n  \"client_id\": \"project-deploy-task\"\n}\nEOF\n\n# 2. Generate .env with all secrets\nsecrets env\n\n# 3. Work with credentials loaded\nsource .env\nnpm run deploy\n\n# 4. Cleanup when done\nsecrets cleanup\n```\n\n**Option 3: One-shot execution**\n```bash\n# Run command with secrets, auto-cleanup\nsecrets exec -- npm run deploy\nsecrets exec -- pytest tests/integration\nsecrets exec -- ./scripts/sync-prod.sh\n```\n\n**Best practices:**\n- Use descriptive `--client-id` values (task name, agent ID)\n- Match TTL to expected task duration\n- Revoke leases when task completes or errors\n- Use `secrets exec` for one-shot commands (auto-cleanup)\n- Use `.secrets.json` for multi-secret workflows\n\n---\n\n### Claude Code / OpenCode (Agent Skills)\n\nInstall the skill documentation so agents understand capabilities and usage patterns.\n\n**Option 1: Global skills (recommended)**\n```bash\n# Install skill globally - available to all projects\nmkdir -p ~/.claude/skills\ncp -r agent-secrets/skills/secret-management ~/.claude/skills/\n\n# For OpenCode\nmkdir -p ~/.opencode/skills\ncp -r agent-secrets/skills/secret-management ~/.opencode/skills/\n```\n\n**Option 2: Per-project skills**\n```bash\n# Add to a specific project\nmkdir -p your-project/.claude/skills\ncp -r agent-secrets/skills/secret-management your-project/.claude/skills/\n```\n\nOnce installed, agents will discover the skill and know how to use the CLI:\n```bash\nsecrets status\nsecrets lease github_token --ttl 1h --client-id \"claude-session-123\"\n```\n\n---\n\n### OpenClaw Plugin\n\nThe repo includes a full OpenClaw plugin with registered tools.\n\n**Installation**\n\nAdd to your OpenClaw config (`~/.openclaw/config.json`):\n\n```json\n{\n  \"plugins\": {\n    \"load\": {\n      \"paths\": [\"~/path/to/agent-secrets\"]\n    },\n    \"entries\": {\n      \"agent-secrets\": {\n        \"enabled\": true,\n        \"config\": {\n          \"default_ttl\": \"1h\",\n          \"client_id_prefix\": \"openclaw\"\n        }\n      }\n    }\n  }\n}\n```\n\nThe plugin assumes `secrets` is in your PATH. Override with `\"cli_path\": \"/custom/path/secrets\"` if needed.\n\n**Registered Tools**\n\n| Tool | Description | Optional |\n|------|-------------|----------|\n| `secrets_lease` | Acquire time-bounded credential | No |\n| `secrets_status` | Check daemon and lease status | No |\n| `secrets_revoke` | Revoke specific lease | No |\n| `secrets_audit` | View audit log | No |\n| `secrets_add` | Add new secret | Yes (allowlist) |\n| `secrets_killswitch` | Emergency revoke all | Yes (allowlist) |\n\n**Enabling optional tools** (dangerous operations require explicit allowlist):\n\n```json\n{\n  \"agents\": {\n    \"list\": [{\n      \"id\": \"main\",\n      \"tools\": {\n        \"allow\": [\"secrets_add\", \"secrets_killswitch\"]\n      }\n    }]\n  }\n}\n```\n\n**Usage in OpenClaw:**\n```\nAgent: I need to access the GitHub token for this task.\n\n[Tool: secrets_lease]\n{\n  \"name\": \"github_token\",\n  \"ttl\": \"30m\",\n  \"client_id\": \"openclaw-task-123\"\n}\n\n→ Returns: ghp_xxxxxxxxxxxx\n```\n\n---\n\n### Direct CLI Usage (Any Agent)\n\nAny agent with shell access can use the CLI directly:\n\n```bash\n# In your agent's environment setup or task preamble\nexport GITHUB_TOKEN=$(secrets lease github_token --ttl 1h --client-id \"my-agent\")\nexport ANTHROPIC_API_KEY=$(secrets lease anthropic_key --ttl 1h --client-id \"my-agent\")\n\n# Check what's available\nsecrets status\n\n# When done or on error, revoke the lease\nsecrets revoke $LEASE_ID\n```\n\n**Best practices for agents:**\n1. Use descriptive `--client-id` values (e.g., `\"claude-refactor-auth-module\"`)\n2. Match TTL to expected task duration\n3. Revoke leases when task completes\n4. Use `secrets audit` to review access patterns\n\n---\n\n## Development\n\n```bash\n# Run tests\nmake test\n\n# Run with coverage\nmake test-cover\n\n# Build\nmake build\n\n# Install to $GOPATH/bin\nmake install\n\n# Lint\nmake lint\n```\n\n## Dependencies\n\n- [filippo.io/age](https://github.com/FiloSottile/age) — Modern encryption\n- [github.com/spf13/cobra](https://github.com/spf13/cobra) — CLI framework\n- [github.com/google/uuid](https://github.com/google/uuid) — Lease IDs\n\n## Inspiration\n\nThis project was inspired by [Alex Hillman's approach](https://x.com/alexhillman/status/2005334574973296911) to agent credential management — the insight that agents shouldn't be anywhere near ALL your passwords. Scoped, time-bounded, audited access with a killswitch.\n\n## License\n\nMIT — Use it, fork it, ship it.\n\n---\n\n*Built for agents that need secrets but shouldn't keep them.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoelhooks%2Fagent-secrets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoelhooks%2Fagent-secrets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoelhooks%2Fagent-secrets/lists"}