{"_id":"@agentlair/verify","name":"@agentlair/verify","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@agentlair/verify","version":"0.1.0","description":"Lightweight AAT (Agent Authentication Token) verification for Node.js, Bun, and edge runtimes. Fetches JWKS from agentlair.dev, caches keys, and validates EdDSA JWTs in one call.","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsc","test":"bun test","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"keywords":["agentlair","aat","jwt","verification","agent-authentication","eddsa","jwks","typescript","ai-agent"],"author":{"name":"AgentLair"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/piiiico/agentlair.git"},"homepage":"https://agentlair.dev","engines":{"node":">=18.0.0"},"dependencies":{"jose":"^5.0.0"},"devDependencies":{"typescript":"^5.9.3"},"_id":"@agentlair/verify@0.1.0","gitHead":"c7fc3c5035f3a88418fbdf5c46cb6f8d4dba4aa4","bugs":{"url":"https://github.com/piiiico/agentlair/issues"},"_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-0MNzYTH3f98U2YhP7/hMaOTkTRQrXjVFHJxvcsSfhw/GfJ+DAFWnKEn6dibJ+jTG5Zi5zMWKRgMAkm689K1iFg==","shasum":"e90e0ecf8281535734ea1118b2e647861091b9d4","tarball":"https://registry.npmjs.org/@agentlair/verify/-/verify-0.1.0.tgz","fileCount":22,"unpackedSize":41040,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFT9jIFuzuF5WRsm+lBA3YehSZCFGXzZspEkhnP9wRnBAiAWmlFyj2ilVKzJtDUsHuPyRO1Cx5xTsoKHtPC/nTfcIQ=="}]},"_npmUser":{"name":"piiiico","email":"pico@amdal.dev"},"directories":{},"maintainers":[{"name":"piiiico","email":"pico@amdal.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/verify_0.1.0_1776716246332_0.26206626082018736"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-20T20:17:26.204Z","0.1.0":"2026-04-20T20:17:26.468Z","modified":"2026-04-20T20:17:26.666Z"},"maintainers":[{"name":"piiiico","email":"pico@amdal.dev"}],"description":"Lightweight AAT (Agent Authentication Token) verification for Node.js, Bun, and edge runtimes. Fetches JWKS from agentlair.dev, caches keys, and validates EdDSA JWTs in one call.","homepage":"https://agentlair.dev","keywords":["agentlair","aat","jwt","verification","agent-authentication","eddsa","jwks","typescript","ai-agent"],"repository":{"type":"git","url":"git+https://github.com/piiiico/agentlair.git"},"author":{"name":"AgentLair"},"bugs":{"url":"https://github.com/piiiico/agentlair/issues"},"license":"MIT","readme":"# @agentlair/verify\n\nLightweight AAT (Agent Authentication Token) verification for Node.js, Bun, Deno, and edge runtimes.\n\nFetches JWKS from agentlair.dev, caches keys, and validates EdDSA JWTs in one function call. Zero configuration needed.\n\n## Install\n\n```bash\nnpm install @agentlair/verify\n# or\nbun add @agentlair/verify\n```\n\n## Quick Start\n\n```typescript\nimport { verifyAAT } from '@agentlair/verify';\n\nconst result = await verifyAAT(token);\n\nif (result.valid) {\n  console.log('Agent ID:', result.agentId);          // \"acc_abc123\"\n  console.log('Agent email:', result.operatorEmail); // \"my-agent@agentlair.dev\"\n  console.log('Scopes:', result.scopes);             // [\"mcp:tools:read\", ...]\n  console.log('Issued at:', result.issuedAt);        // Date\n  console.log('Expires at:', result.expiresAt);      // Date\n} else {\n  console.error('Token rejected:', result.error);\n}\n```\n\n## API\n\n### `verifyAAT(token, options?)`\n\nVerifies an AgentLair AAT. Fetches JWKS from agentlair.dev, caches keys, and validates the EdDSA signature and JWT claims.\n\n```typescript\nconst result = await verifyAAT(token, {\n  jwksUrl: 'https://agentlair.dev/.well-known/jwks.json', // default\n  audience: 'https://my-service.example.com',             // optional: enforce aud claim\n  maxAge: '1h',                                           // optional: reject tokens older than 1 hour\n  cacheTtl: 300_000,                                      // JWKS cache TTL in ms (default: 5 min)\n  requiredClaims: { iss: 'https://agentlair.dev' },       // optional: additional claim checks\n});\n```\n\n**Returns:** `VerifyResult`\n\n```typescript\n// On success:\n{\n  valid: true,\n  agentId: string,         // sub claim — AgentLair account ID\n  operatorEmail: string | undefined,  // al_email claim\n  issuedAt: Date,\n  expiresAt: Date,\n  scopes: string[],        // al_scopes claim\n  claims: AATClaims,       // full decoded payload for advanced use\n}\n\n// On failure:\n{\n  valid: false,\n  error: string,           // human-readable reason\n}\n```\n\n### `clearJWKSCache()`\n\nClears the module-level JWKS cache. Useful in tests or when forcing a key refresh.\n\n```typescript\nimport { clearJWKSCache } from '@agentlair/verify';\nclearJWKSCache();\n```\n\n## Middleware\n\n### Express\n\n```typescript\nimport express from 'express';\nimport { createExpressMiddleware } from '@agentlair/verify';\n\nconst app = express();\n\n// Protect all /api routes\napp.use('/api', createExpressMiddleware({\n  audience: 'https://my-api.example.com',\n}));\n\napp.get('/api/data', (req, res) => {\n  // req.aat is set after successful verification\n  console.log('Agent:', req.aat?.agentId);\n  console.log('Scopes:', req.aat?.scopes);\n  res.json({ ok: true });\n});\n```\n\nTypeScript: extend the request type to get autocomplete:\n\n```typescript\ndeclare global {\n  namespace Express {\n    interface Request {\n      aat?: import('@agentlair/verify').VerifyResult & { valid: true };\n    }\n  }\n}\n```\n\n### Hono\n\n```typescript\nimport { Hono } from 'hono';\nimport { createHonoMiddleware } from '@agentlair/verify';\n\nconst app = new Hono<{\n  Variables: { aat: import('@agentlair/verify').VerifyResult & { valid: true } }\n}>();\n\napp.use('/api/*', createHonoMiddleware({\n  audience: 'https://my-api.example.com',\n}));\n\napp.get('/api/data', (c) => {\n  const aat = c.get('aat');\n  return c.json({ agentId: aat.agentId, scopes: aat.scopes });\n});\n```\n\n### Fastify\n\n```typescript\nimport Fastify from 'fastify';\nimport { createFastifyHook } from '@agentlair/verify';\n\nconst fastify = Fastify();\n\nfastify.addHook('preHandler', createFastifyHook({\n  audience: 'https://my-api.example.com',\n}));\n\nfastify.get('/api/data', async (request) => {\n  console.log('Agent:', request.aat?.agentId);\n  return { ok: true };\n});\n```\n\n## How it works\n\n1. The JWT header contains a `kid` (key ID).\n2. `@agentlair/verify` fetches `https://agentlair.dev/.well-known/jwks.json` and caches the response for 5 minutes (configurable).\n3. The matching JWK is selected by `kid` — key ID matching, not array position. This means key rotation is seamless.\n4. The EdDSA (Ed25519) signature is verified using the public key.\n5. Standard JWT claims (`exp`, `iat`, `iss`) and AgentLair-specific claims (`al_scopes`, `al_audit_url`) are validated.\n\n## Error messages\n\nClear error messages for common failure modes:\n\n| Situation | Error |\n|-----------|-------|\n| Token expired | `Token expired at 2026-04-20T12:00:00.000Z` |\n| Wrong issuer | `Invalid claim \"iss\": check failed` |\n| Audience mismatch | `Invalid claim \"aud\": check failed` |\n| Bad signature | `Signature verification failed: token may be tampered or signed with wrong key` |\n| Unknown key | `No matching key in JWKS: key ID not found or key has been rotated` |\n| JWKS fetch failed | `Failed to fetch JWKS: <network error>` |\n| Malformed JWT | `Malformed token: expected 3-part JWT (header.payload.signature)` |\n\n## Types\n\n```typescript\nimport type {\n  AATClaims,      // Full JWT payload type\n  VerifyOptions,  // Options for verifyAAT()\n  VerifyResult,   // Return type of verifyAAT()\n  MiddlewareOptions, // Options for middleware factories\n} from '@agentlair/verify';\n```\n\n### `AATClaims`\n\n```typescript\ninterface AATClaims {\n  // Standard JWT\n  iss: string;          // \"https://agentlair.dev\"\n  sub: string;          // AgentLair account ID\n  aud: string;          // Target audience URL\n  exp: number;          // Expiration (Unix seconds)\n  iat: number;          // Issued at (Unix seconds)\n  jti: string;          // Unique token ID\n\n  // AgentLair-specific\n  al_scopes: string[];  // Granted scopes\n  al_audit_url: string; // Audit trail link\n  al_name?: string;     // Agent name\n  al_email?: string;    // Agent email (@agentlair.dev)\n\n  // MCP-I Level 2 interop\n  did?: string;         // e.g. \"did:web:agentlair.dev:agents:acc_xxx\"\n\n  // Trust attestation (RFC-001 Phase 1)\n  al_trust?: {\n    score: number;       // [0, 100]\n    level: 'intern' | 'junior' | 'senior' | 'principal';\n    confidence: number;  // [0.0, 1.0]\n    computed_at: string; // ISO 8601\n    trend: 'improving' | 'stable' | 'declining';\n  };\n}\n```\n\n## Requirements\n\n- Node.js ≥ 18 (Web Crypto API required)\n- Bun (any version)\n- Deno (any version)\n- Edge runtimes: Cloudflare Workers, Vercel Edge Functions, etc.\n\n## License\n\nMIT — [AgentLair](https://agentlair.dev)\n","readmeFilename":"README.md","_rev":"1-e8b6b911e4fb48f67891950546a03dad"}