Skip to main content
Glama
SanjoyDat1

latchkey

by SanjoyDat1
README.md
# latchkey

**Capability-token gate for AI agents.** Mint time-boxed, scope-limited grants ("latchkeys") that authorize specific tool calls. Agents present a key; the gate verifies TTL, scopes, audience, and spend limits before allowing the call.

Full MCP surface for mint / revoke / introspect / authorize — plus CLI and a hash-chained audit log.

## Why

Agent tool access is often all-or-nothing. Latchkey lets you hand an agent a **narrow, expiring, use-capped** credential instead of ambient authority — including **delegation chains** (A mints a tighter key for B) and an emergency **kill-switch**.

## Features

- HMAC-signed tokens (`lk1.…`) via `LATCHKEY_SECRET` — no external KMS required
- Scopes: `tool:http_request`, `tool:file_write:path=/tmp/**`, `risk:irreversible`
- Use-once keys (`maxUses: 1`) and spend limits
- Delegation with narrow-only MintPolicy
- Revocation list + revoke-all kill-switch
- Hash-chained audit log + Mermaid grant-graph export
- MCP tools + CLI

## Packages

| Package | Role |
|---------|------|
| `@latchkey/core` | LatchKey, KeyStore, MintPolicy, Authorizer, RevocationList, AuditLog |
| `@latchkey/mcp-server` | Stdio MCP server |
| `@latchkey/cli` | `latchkey` CLI |

## Quick start

```bash
git clone https://github.com/SanjoyDat1/latchkey.git
cd latchkey
npm install
cp .env.example .env   # set LATCHKEY_SECRET
export LATCHKEY_SECRET="$(openssl rand -hex 32)"
npm run build
npm test
npm run typecheck
```

### CLI

```bash
export LATCHKEY_SECRET="$(openssl rand -hex 32)"

npx latchkey mint \
  --audience coding-agent \
  --scope 'tool:file_write:path=/tmp/**' \
  --scope 'tool:http_request' \
  --ttl-ms 3600000 \
  --max-uses 20

npx latchkey authorize --token "$TOKEN" --tool file_write --param path=/tmp/out.txt --audience coding-agent
npx latchkey export --format mermaid
npx latchkey revoke --all   # emergency kill-switch
```

Use `LATCHKEY_STORE_PATH` so mint/authorize share state across CLI processes.
## Cursor MCP config

Add to your Cursor MCP settings (stdio; logs go to stderr only):

```json
{
  "mcpServers": {
    "latchkey": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/latchkey/packages/mcp-server/dist/index.js"],
      "env": {
        "LATCHKEY_SECRET": "replace-with-long-random-secret-32chars-min",
        "LATCHKEY_ADMIN_TOKEN": "replace-with-admin-token-16chars-min",
        "LATCHKEY_STORE_PATH": "/ABSOLUTE/PATH/TO/latchkey/.latchkey/store.json",
        "LATCHKEY_AUDIT_PATH": "/ABSOLUTE/PATH/TO/latchkey/.latchkey/audit.json"
      }
    }
  }
}
```

Admin tools (`lk_mint`, `lk_revoke`, `lk_list_active`, `lk_audit_query`, `lk_export`) require `adminToken` matching `LATCHKEY_ADMIN_TOKEN`. `lk_authorize` / `lk_introspect` only need a latchkey token (authorize also requires `audience`).

### MCP tools

| Tool | Purpose |
|------|---------|
| `lk_mint` | Mint a latchkey (optional `parentToken` for delegation) |
| `lk_revoke` | Revoke by id/token, or `all` kill-switch |
| `lk_introspect` | Status without consuming a use |
| `lk_authorize` | Gate a tool call (consumes a use on allow) |
| `lk_list_active` | List live keys |
| `lk_audit_query` | Query hash-chained audit events |
| `lk_export` | Mermaid grant graph or JSON snapshot |

## Token format

```
lk1.<base64url(payload)>.<base64url(hmac_sha256)>
```

Signed with `HMAC-SHA256(LATCHKEY_SECRET, "lk1." + payload)`. Verification uses constant-time comparison.

**Secret:** set `LATCHKEY_SECRET` (min 32 characters). Set `LATCHKEY_ADMIN_TOKEN` for MCP admin tools. Never commit secrets. Rotate by minting new keys and revoking old ones.

## Architecture

See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/PLAN.md](docs/PLAN.md).

```mermaid
flowchart LR
  Host[Agent Host] -->|MCP| Server[latchkey-mcp]
  Server --> Core[Authorizer / MintPolicy]
  Core --> Store[KeyStore]
  Core --> Rev[RevocationList]
  Core --> Audit[AuditLog]
```

## License

MIT