{"id":46228773,"url":"https://github.com/openleash/openleash","last_synced_at":"2026-04-15T18:04:21.921Z","repository":{"id":339030478,"uuid":"1159016876","full_name":"openleash/openleash","owner":"openleash","description":"An open-source authorization layer where owners set policies, agents ask before acting, and counterparties can verify the agent was authorized.","archived":false,"fork":false,"pushed_at":"2026-04-13T16:26:37.000Z","size":1660,"stargazers_count":10,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-13T17:45:21.067Z","etag":null,"topics":["ai-agents","ai-safety","authorization","cryptography","local-first","nodejs","paseto","policy-engine","sidecar","typescript"],"latest_commit_sha":null,"homepage":"https://openleash.ai","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openleash.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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},"funding":{"github":["openleash"]}},"created_at":"2026-02-16T07:55:43.000Z","updated_at":"2026-04-13T16:26:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/openleash/openleash","commit_stats":null,"previous_names":["openleash/openleash"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/openleash/openleash","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openleash%2Fopenleash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openleash%2Fopenleash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openleash%2Fopenleash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openleash%2Fopenleash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openleash","download_url":"https://codeload.github.com/openleash/openleash/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openleash%2Fopenleash/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31853282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"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":["ai-agents","ai-safety","authorization","cryptography","local-first","nodejs","paseto","policy-engine","sidecar","typescript"],"created_at":"2026-03-03T17:15:27.750Z","updated_at":"2026-04-15T18:04:21.897Z","avatar_url":"https://github.com/openleash.png","language":"TypeScript","funding_links":["https://github.com/sponsors/openleash"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"https://openleash.ai/brand/openleash-mark.svg\" alt=\"OpenLeash logo\" width=\"80\" /\u003e\n\n# OpenLeash\n\n🔐 **Authorization guardrails for AI agents.** 🦞\n\n[![CI](https://img.shields.io/github/actions/workflow/status/openleash/openleash/ci.yml?branch=main\u0026style=for-the-badge\u0026label=CI)](https://github.com/openleash/openleash/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@openleash/core?style=for-the-badge\u0026label=npm)](https://www.npmjs.com/package/@openleash/core)\n[![License](https://img.shields.io/github/license/openleash/openleash?style=for-the-badge)](LICENSE)\n[![Discussions](https://img.shields.io/github/discussions/openleash/openleash?style=for-the-badge)](https://github.com/openleash/openleash/discussions)\n\n[📖 Docs](docs/) \u0026bull; [🚀 Getting Started](#quickstart) \u0026bull; [📦 npm](https://www.npmjs.com/org/openleash) \u0026bull; [💬 Discussions](https://github.com/openleash/openleash/discussions)\n\n\u003c/div\u003e\n\n---\n\n## What is OpenLeash?\n\nAn open-source authorization layer where owners set policies, agents ask before acting, and counterparties can verify the agent was authorized.\n\nOpenLeash runs locally next to your AI agent runtime. Before an agent takes a side-effectful action (purchases, bookings, sending messages, government submissions), it asks OpenLeash. The server evaluates the request against YAML policies and returns a decision (`ALLOW`, `DENY`, `REQUIRE_APPROVAL`, `REQUIRE_STEP_UP`, `REQUIRE_DEPOSIT`), a list of obligations, and optionally a short-lived proof token (PASETO v4.public) that counterparties can verify.\n\n## ⚡ Quickstart\n\n```bash\n# Clone and build\ngit clone https://github.com/openleash/openleash.git \u0026\u0026 cd openleash\nnpm install \u0026\u0026 npm run build\n\n# Start the server (bootstraps ./data and config.yaml)\nnpx openleash start\n\n# Run the interactive setup wizard\nnpx openleash wizard\n```\n\n## 🔧 SDK Usage\n\n```typescript\nimport { authorize } from '@openleash/sdk-ts';\n\nconst result = await authorize({\n  openleashUrl: 'http://127.0.0.1:8787',\n  agentId: 'my-agent',\n  privateKeyB64: process.env.OPENLEASH_AGENT_PRIVATE_KEY_B64!,\n  action: {\n    action_id: crypto.randomUUID(),\n    action_type: 'purchase',\n    requested_at: new Date().toISOString(),\n    principal: { agent_id: 'my-agent' },\n    subject: { principal_id: '\u003cowner-id\u003e' },\n    relying_party: { domain: 'example.com', trust_profile: 'LOW' },\n    payload: { amount_minor: 5000, currency: 'USD', merchant_domain: 'example.com' },\n  },\n});\n\nconsole.log(result);\n```\n\nVerify a proof offline:\n\n```typescript\nimport { verifyProofOffline } from '@openleash/sdk-ts';\n\nconst result = await verifyProofOffline({\n  token: proofToken,\n  publicKeys: [{ kid: 'key-id', public_key_b64: 'base64...' }],\n});\n\nconsole.log(result.valid, result.claims);\n```\n\n## 🧪 Playground\n\nRun predefined scenarios to test policy behavior:\n\n```bash\nnpx openleash playground list\nnpx openleash playground run small_purchase_allowed\nnpx openleash playground run large_purchase_requires_approval\n```\n\n## 📋 CLI Commands\n\n| Command | Description |\n|---|---|\n| `openleash start` | Start the server |\n| `openleash wizard` | Interactive setup wizard |\n| `openleash policy list` | List policies |\n| `openleash policy show \u003cid\u003e` | Show policy YAML |\n| `openleash policy upsert --owner \u003cid\u003e --file \u003cpath\u003e` | Create/update policy |\n| `openleash policy validate --file \u003cpath\u003e` | Validate policy YAML |\n| `openleash playground list` | List scenarios |\n| `openleash playground run \u003cname\u003e` | Run a scenario |\n| `openleash keys list` | List signing keys |\n| `openleash keys rotate` | Rotate signing key |\n| `openleash testvectors` | Generate test vectors |\n\n## 📖 API Reference\n\nWhen the server is running, an interactive API reference and machine-readable OpenAPI spec are available:\n\n- **Interactive reference:** `http://localhost:8787/reference`\n- **OpenAPI spec (JSON):** `http://localhost:8787/reference/openapi.json`\n\nThe `/v1/health` endpoint also includes these URLs in its response.\n\n## 🏗️ Architecture\n\n```\npackages/\n  core/       # Authorization engine, types, crypto, state management\n  server/     # Fastify HTTP server, routes, middleware\n  gui/        # Server-rendered HTML GUI (admin + owner portal)\n  sdk-ts/     # TypeScript SDK for agents and counterparties\n  cli/        # CLI commands (start, wizard, policy, playground, keys)\n```\n\nFour actor types interact with the API:\n\n| Actor | Auth | Endpoints |\n|---|---|---|\n| **Public** | None | `/v1/health`, `/v1/public-keys`, `/v1/verify-proof` |\n| **Agent** | Ed25519 request signing | `/v1/authorize`, `/v1/agent/*` |\n| **Owner** | PASETO session token | `/v1/owner/*` |\n| **Admin** | Bearer token / localhost | `/v1/admin/*` |\n\nAll state is stored in human-readable files:\n- `./data/state.md` — authoritative index (markdown with YAML)\n- `./data/owners/` — owner profiles (markdown with YAML frontmatter)\n- `./data/agents/` — agent records (markdown with YAML frontmatter)\n- `./data/policies/` — policy YAML files\n- `./data/keys/` — signing key JSON files\n- `./data/approval-requests/` — approval request records\n- `./data/policy-drafts/` — agent-proposed policy drafts\n- `./data/invites/` — owner setup invites\n- `./data/agent-invites/` — agent registration invites\n- `./data/audit.log.jsonl` — append-only audit log\n\n## 🔑 Approval Workflow\n\nWhen a policy includes a `HUMAN_APPROVAL` obligation, agents must get explicit owner approval:\n\n```\nAgent → POST /v1/authorize           → REQUIRE_APPROVAL\nAgent → POST /v1/agent/approval-requests  → Creates pending request\nOwner → POST /v1/owner/.../approve   → Issues approval token\nAgent → POST /v1/authorize (+ token) → ALLOW + proof token\n```\n\nApproval tokens are single-use, action-scoped, and time-limited. See [docs/protocol.md](docs/protocol.md) for details.\n\n## 📝 Policy Drafts\n\nAgents can propose new policies to their owner when they need access to action types not covered by existing rules:\n\n```\nAgent → POST /v1/agent/policy-drafts      → Submits draft YAML + justification\nOwner → GET  /v1/owner/policy-drafts       → Reviews pending drafts\nOwner → POST /v1/owner/.../approve         → Creates real policy + binding\nAgent → GET  /v1/agent/policy-drafts/:id   → Sees APPROVED + resulting_policy_id\n```\n\nThis lets agents self-serve within the owner's control — the owner always has the final say. See [docs/protocol.md](docs/protocol.md#policy-drafts) for the full specification.\n\n## 👤 Owner Portal\n\nThe owner portal is a self-service web interface where owners can manage their policies, review pending approval requests, and view registered agents. Access it at `/gui/login`.\n\n**Setup flow:**\n\n1. An admin creates an owner via the Admin Dashboard (`/gui/dashboard`) or `npx openleash wizard`\n2. The admin generates a setup invite — the GUI produces a copyable setup link\n3. The owner opens the link (`/gui/owner-setup?invite_id=...\u0026invite_token=...`) and chooses a passphrase\n4. After setup, the owner is offered to create an **agent invite** — a single URL that an agent uses to register itself\n5. The owner logs in at `/gui/login` with their Owner Principal ID and passphrase\n\n## 🤖 Agent Registration\n\nAgents register via **invite URLs** created by owners (or admins). The invite URL is self-contained — the agent POSTs its public key and agent ID to it, and receives back its identity, the signing protocol, all available endpoints, and SDK install instructions.\n\n**Using the TypeScript SDK:**\n\n```typescript\nimport { redeemAgentInvite } from '@openleash/sdk-ts';\n\nconst agent = await redeemAgentInvite({\n  inviteUrl: process.env.OPENLEASH_AGENT_INVITE_URL!,\n  agentId: 'my-agent',\n});\n// agent.openleash_url, agent.private_key_b64, agent.agent_principal_id, ...\n```\n\nOwners can create agent invites from:\n- The owner setup page (offered immediately after setting a passphrase)\n- The owner agents page (`/gui/admin/agents`)\n- The admin agents page (`/gui/admin/agents`)\n\nSee [AGENTS.md](AGENTS.md) for the full agent integration guide.\n\n## 🔍 Troubleshooting\n\n### NO_POLICY errors\n\nIf an agent gets a `403` with `NO_POLICY`, it means no policy is bound to the agent or its owner. Create a policy and bind it via the owner portal or `npx openleash policy upsert`.\n\n### Clock skew errors\n\nIf you get `TIMESTAMP_SKEW` errors, ensure the requesting system's clock is synchronized. The default allowed skew is ±120 seconds. Adjust in `config.yaml`:\n\n```yaml\nsecurity:\n  clock_skew_seconds: 300\n```\n\n### Nonce replay errors\n\nEach nonce can only be used once per agent within the TTL window (default 600 seconds). Generate a unique nonce (UUID) for each request.\n\n### Invalid signature errors\n\n- Ensure you're using the correct private key (PKCS8 DER base64 format)\n- The signing input must be exactly: `METHOD\\nPATH\\nTIMESTAMP\\nNONCE\\nBODY_SHA256`\n- The body SHA256 must match the raw request body bytes\n\n### Admin token confusion\n\n- Default admin mode is `localhost_or_token`: localhost requests bypass token check\n- If accessing remotely, you need `admin.allow_remote_admin: true` and a valid bearer token\n- Token is stored in `config.yaml` under `admin.token`\n\n### Data folder location\n\nOpenLeash stores all state in `./data/` relative to where you run the command. Make sure you run all commands from the same directory.\n\n## 🤝 Contributing\n\nContributions are welcome! Please read the [Contributing Guide](CONTRIBUTING.md) before submitting a pull request.\n\n## 📄 License\n\n[Apache-2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenleash%2Fopenleash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenleash%2Fopenleash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenleash%2Fopenleash/lists"}