{"_id":"@abaxxlabs/agents","name":"@abaxxlabs/agents","dist-tags":{"latest":"0.11.4"},"versions":{"0.11.4":{"name":"@abaxxlabs/agents","version":"0.11.4","description":"Agents++ — encryption-based agent identity and scoped database access for PostgreSQL","type":"module","main":"./dist/cjs/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/cjs/index.js"}},"./storage":{"import":{"types":"./dist/storage/index.d.ts","default":"./dist/storage/index.js"},"require":{"types":"./dist/storage/index.d.ts","default":"./dist/cjs/storage/index.js"}},"./sqlite":{"import":{"types":"./dist/storage/sqlite/index.d.ts","default":"./dist/storage/sqlite/index.js"},"require":{"types":"./dist/storage/sqlite/index.d.ts","default":"./dist/cjs/storage/sqlite/index.js"}},"./bootstrap":{"import":{"types":"./dist/bootstrap/index.d.ts","default":"./dist/bootstrap/index.js"},"require":{"types":"./dist/bootstrap/index.d.ts","default":"./dist/cjs/bootstrap/index.js"}},"./sql":{"import":{"types":"./dist/sql/index.d.ts","default":"./dist/sql/index.js"},"require":{"types":"./dist/sql/index.d.ts","default":"./dist/cjs/sql/index.js"}},"./mcp":{"import":{"types":"./dist/mcp/index.d.ts","default":"./dist/mcp/index.js"},"require":{"types":"./dist/mcp/index.d.ts","default":"./dist/cjs/mcp/index.js"}},"./id-sdk-mcp":{"import":{"types":"./dist/id-sdk-mcp.d.ts","default":"./dist/id-sdk-mcp.js"},"require":{"types":"./dist/id-sdk-mcp.d.ts","default":"./dist/cjs/id-sdk-mcp.js"}}},"bin":{"agents":"dist/cli/index.js"},"engines":{"node":">=20.3.0"},"scripts":{"build":"npm run build:esm && npm run build:cjs && npm run build:cjs-shim","build:esm":"tsc","build:cjs":"tsc -p tsconfig.cjs.json","build:cjs-shim":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json","clean":"rm -rf dist","dev":"tsc --watch","test":"vitest run","test:watch":"vitest","test:e2e":"vitest run --config vitest.e2e.config.ts","assert:package-artifacts":"node scripts/assert-package-artifacts.mjs","audit:package-files":"node scripts/audit-public-artifacts.mjs package","audit:public-repo":"node scripts/audit-public-artifacts.mjs public-repo","audit:public-artifacts":"node scripts/audit-public-artifacts.mjs all","lint":"eslint src/ test/ packages/server/src/ --ext .ts","lint:fix":"eslint src/ test/ packages/server/src/ --ext .ts --fix","format":"prettier --write 'src/**/*.ts' 'test/**/*.ts'","demo":"node --loader ts-node/esm src/cli/index.ts demo","typecheck":"tsc --noEmit","release:check-npm-cache":"node scripts/check-npm-cache.mjs","benchmark:query":"npm run build:esm && node scripts/benchmark-query-path.mjs","check:public-api":"node scripts/check-public-api.mjs","update:public-api":"node scripts/check-public-api.mjs --update","release:gate":"node scripts/release-gate.mjs","prepublishOnly":"npm run clean && npm run build"},"keywords":["agent","identity","DID","verifiable-credentials","encryption","postgres","middleware","scope"],"license":"Apache-2.0","publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"repository":{"type":"git","url":"git+https://github.com/abaxxlabs/agents.git"},"dependencies":{"canonicalize":"2.1.0","commander":"12.1.0","uuid":"14.0.0","zod":"4.3.6"},"optionalDependencies":{"@anthropic-ai/sdk":"0.91.1","@modelcontextprotocol/sdk":"1.29.0"},"overrides":{"@modelcontextprotocol/sdk":{"@hono/node-server":"1.19.14","hono":"4.12.15"},"@typescript-eslint/typescript-estree":{"minimatch":{"brace-expansion":"5.0.5"}},"minimatch":{"brace-expansion":"1.1.14"},"router":{"path-to-regexp":"8.4.2"},"vite":{"postcss":"8.5.12"}},"peerDependencies":{"better-sqlite3":"11.10.0","libpg-query":"17.7.3","pg":"8.20.0"},"peerDependenciesMeta":{"better-sqlite3":{"optional":true},"libpg-query":{"optional":true},"pg":{"optional":true}},"devDependencies":{"@types/better-sqlite3":"^7.6.13","@types/bun":"1.1.6","@types/express":"^5.0.6","@types/node":"^22.0.0","@types/pg":"^8.11.0","@types/uuid":"^10.0.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","better-sqlite3":"11.10.0","eslint":"^9.0.0","express":"^5.2.1","libpg-query":"17.7.3","pg":"8.20.0","prettier":"^3.3.0","ts-node":"^10.9.0","typescript":"^5.6.0","vite":"6.4.2","vitest":"4.0.18"},"gitHead":"fb740cb7504dbac6784a26063886806e2d4dd324","_id":"@abaxxlabs/agents@0.11.4","bugs":{"url":"https://github.com/abaxxlabs/agents/issues"},"homepage":"https://github.com/abaxxlabs/agents#readme","_nodeVersion":"25.8.0","_npmVersion":"11.11.0","dist":{"integrity":"sha512-Xa1v4NWBYBKxLmOFXDtRJBERNn/V10iLFaXE+tRdM3i1Y//sIkgvDlTYSjRQfBSHjzHAqg039okzMjCJZNSKEQ==","shasum":"d81acd6d072a7da5a2901a9f3f5c2eb902e3b6f3","tarball":"https://registry.npmjs.org/@abaxxlabs/agents/-/agents-0.11.4.tgz","fileCount":906,"unpackedSize":4268831,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCKOnLU66nggp027d0acLhRJWB+8NTt34lhWQ23feJQlQIhAPSu088ECYVteYXJIfyCGWrIeTVmdH439QSu+unypRRu"}]},"_npmUser":{"name":"ian-abaxx","email":"ian@abaxx.tech"},"directories":{},"maintainers":[{"name":"ian-abaxx","email":"ian@abaxx.tech"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agents_0.11.4_1777608583483_0.0802568167765263"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-01T04:09:43.378Z","0.11.4":"2026-05-01T04:09:43.684Z","modified":"2026-05-01T04:09:43.920Z"},"maintainers":[{"name":"ian-abaxx","email":"ian@abaxx.tech"}],"description":"Agents++ — encryption-based agent identity and scoped database access for PostgreSQL","homepage":"https://github.com/abaxxlabs/agents#readme","keywords":["agent","identity","DID","verifiable-credentials","encryption","postgres","middleware","scope"],"repository":{"type":"git","url":"git+https://github.com/abaxxlabs/agents.git"},"bugs":{"url":"https://github.com/abaxxlabs/agents/issues"},"license":"Apache-2.0","readme":"# Agents++\n\n[![npm](https://img.shields.io/npm/v/@abaxxlabs/agents.svg)](https://www.npmjs.com/package/@abaxxlabs/agents)\n[![CI](https://github.com/abaxxlabs/agents/actions/workflows/ci.yml/badge.svg)](https://github.com/abaxxlabs/agents/actions/workflows/ci.yml)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n\n**Column-level access control for AI agents — enforced in the query layer, not the application.**\n\nTwo agents query the same table. They get completely different data — not because your code filters it, but because the query layer physically cannot return what the agent isn't credentialed for.\n\n## The problem with application-level checks\n\nAI agents get prompt-injected. Permission checks that happen before the query are bypassable — a compromised or manipulated agent can work around them entirely. Once data leaves the database unfiltered, you've already lost.\n\nAgents++ moves enforcement into the query layer. Each agent carries a signed, scoped [Verifiable Credential](https://www.w3.org/TR/vc-data-model/). The ScopeEngine verifies it on every query, enforces the projection boundary, and decrypts only the authorized columns — before results leave the database.\n\n## How it works\n\n```mermaid\nflowchart LR\n    H[\"👤 Human\\n(OIDC auth)\"]\n    H -->|\"issues credential\\ncolumns: price, qty\"| A\n    H -->|\"issues credential\\ncolumns: instrument\"| B\n    A[\"🤖 Agent A\"] -->|query| SE\n    B[\"🤖 Agent B\"] -->|query| SE\n    SE[\"ScopeEngine\\n─────────────\\n✓ verify credential\\n✓ enforce projection\\n✓ decrypt columns\\n✓ sign audit record\"]\n    SE --> DB[(\"PostgreSQL\\nAES-256-GCM\\nper column\")]\n    SE --> AL[\"Audit trail\\nEd25519-signed\\nhash-chained\"]\n```\n\nSame table. Same SQL. Agent A gets `price` and `quantity` in cleartext. Agent B gets `instrument` only. Querying an out-of-scope encrypted column throws `ScopeViolationError` — it isn't filtered, it's rejected before execution.\n\n## What you get\n\n- **Verifiable Credentials** — W3C-standard, signed, expiring, bound to the agent's DID\n- **Column-level encryption** — AES-256-GCM per column, BYOK master key, atomic key rotation\n- **Tamper-proof audit trail** — Ed25519-signed records, PostgreSQL-enforced append-only, hash-chained\n- **Agent delegation** — supervisors can delegate a strict subset of their scope to workers, TTL-capped\n- **MCP-native** — Claude, GPT, and any MCP-compatible agent works out of the box\n- **Process isolation** — run as a separate process; the agent never touches DB credentials or the master key\n- **Multiple backends** — PostgreSQL, SQLite, or in-memory storage; swap without changing application code\n\n## Before / after\n\n**Before** — trust your agent not to exceed its permissions:\n\n```typescript\n// Application checks before the query — a prompt-injected agent can route around these\nif (!agent.hasPermission('price')) throw new Error('denied');\nconst result = await db.query('SELECT instrument, price FROM orders');\n```\n\n**After** — the query layer makes it structurally impossible:\n\n```typescript\n// Agent presents a signed credential — ScopeEngine verifies and enforces on every query\nconst result = await scope.query({\n  agent: agent.did,\n  credential,           // VC: { columns: ['orders.instrument'], actions: ['read'] }\n  table: 'orders',\n  sql: 'SELECT instrument, price FROM orders',\n  // 'price' is encrypted and out of scope → ScopeViolationError before execution\n});\n```\n\n## Install\n\n```bash\nnpm install @abaxxlabs/agents\n```\n\nPeer dependencies for SQL enforcement:\n\n```bash\nnpm install pg libpg-query\n```\n\n## Subpath exports\n\n| Import | What it provides |\n|--------|-----------------|\n| `@abaxxlabs/agents` | AgentIdentity, auth, credentials, crypto, storage interfaces |\n| `@abaxxlabs/agents/sql` | AgentScope, ScopeEngine, column-key management |\n| `@abaxxlabs/agents/mcp` | MCP server factory and `agents mcp` CLI |\n| `@abaxxlabs/agents/storage` | Storage backend composition |\n| `@abaxxlabs/agents/sqlite` | SQLite backend (bun:sqlite / better-sqlite3) |\n| `@abaxxlabs/agents/bootstrap` | `resolveMasterKeyFromEnv()` helper |\n| `@abaxxlabs/agents/id-sdk-mcp` | Platform identity adapter (AbaxxOne) |\n\n## Quick start\n\n```typescript\nimport { AgentScope } from '@abaxxlabs/agents/sql';\nimport { resolveMasterKeyFromEnv } from '@abaxxlabs/agents/bootstrap';\n\nconst scope = await AgentScope.create(\n  {\n    database: { connectionString: process.env.DATABASE_URL },\n    encryption: { columns: ['orders.quantity', 'orders.price', 'orders.counterparty'] },\n    audit: { enabled: true },\n  },\n  { masterKey: resolveMasterKeyFromEnv() },\n);\n\n// Authenticate (mock in dev, OIDC in production)\nconst session = await scope.authenticate({ mockHumanDid: 'alice' });\n\n// Create an agent with a DID\nconst agent = await scope.createAgent({ name: 'trading-agent', ownerDid: session.humanDid });\n\n// Issue a scoped credential — alice decides what the agent can see\nconst credential = await session.issueCredential({\n  agent: agent.did,\n  columns: ['orders.instrument', 'orders.quantity'],\n  actions: ['read'],\n  expiresIn: '4h',\n});\n\n// Query — only authorized columns are decrypted\nconst result = await scope.query({\n  agent: agent.did,\n  credential,\n  table: 'orders',\n  sql: 'SELECT instrument, quantity FROM orders',\n});\n// Requesting 'price' or 'counterparty' → ScopeViolationError\n```\n\n## MCP server\n\nAI agents connect directly via the [Model Context Protocol](https://modelcontextprotocol.io):\n\n```bash\n# stdio mode — Claude Desktop, Claude Code\nagents mcp --db postgresql://localhost/mydb --mock \"alice\"\n\n# HTTP mode — remote agents, master key stays in this process\nagents mcp --db postgresql://localhost/mydb --transport http --port 8443 \\\n  --tls-cert cert.pem --tls-key key.pem\n```\n\n**Claude Desktop** (`claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"agents\": {\n      \"command\": \"npx\",\n      \"args\": [\"@abaxxlabs/agents\", \"mcp\", \"--db\", \"postgresql://localhost/mydb\", \"--mock\", \"alice\"]\n    }\n  }\n}\n```\n\nUse HTTP transport for production — the agent calls over the network and never has direct access to the database or master key.\n\n## Delegation\n\nSupervisors can delegate a strict subset of their scope to workers:\n\n```typescript\nconst workerCred = scope.delegateCredential(supervisor.did, supervisorCred, {\n  targetAgent: worker.did,\n  columns: ['orders.instrument'],   // must be a subset of supervisor's scope\n  actions: ['read'],\n  expiresIn: '1h',                  // capped at supervisor's remaining TTL\n});\n```\n\n## Security model\n\n- **Ed25519 only** — no algorithm agility, no downgrade surface\n- **BYOK master key** — Agents++ never reads `process.env`; you pass the key explicitly. `MasterKey` is a branded type that blocks the buffer from leaking into untyped sinks at compile time\n- **Wrong-key boots fail loud** — `AgentScope.create` throws `MasterKeyMismatchError` immediately if existing column keys can't be decrypted; no silent `[ENCRYPTED]` placeholders\n- **Column encryption** — AES-256-GCM per column; `rotateColumnKey()` re-encrypts all rows atomically; `rewrapColumnKey()` migrates to a new master key without touching row data\n- **Append-only audit trail** — PostgreSQL triggers block UPDATE/DELETE; every record is Ed25519-signed and hash-chained against the previous\n- **VP audience binding** — credentials can be bound to a specific server DID, preventing replay across instances\n- **PKCE S256** on all OIDC flows; SSRF guards on discovered endpoints\n- **Mock auth gated** — `mockHumanDid` only works in `NODE_ENV=development` or `test`\n\nSee [`docs/DECISIONS.md`](docs/DECISIONS.md) for architecture decision records.\n\n## Development\n\nRequires Node.js 20+ and PostgreSQL 16 for the full test suite (Postgres-gated tests skip gracefully without a live database).\n\n```bash\nnpm install\nnpm test          # 1,287 tests, ~15s\nnpm run build     # TypeScript → dist/\nnpm run typecheck\n```\n\nTo run Postgres-gated tests locally:\n\n```bash\ndocker run -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16-alpine\n\nDATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres \\\n  node scripts/setup-test-db.mjs\n\nDATABASE_URL=... npm test\n```\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE).\n","readmeFilename":"README.md","_rev":"1-ae0b2540f33595b2e4db163d1e58c233"}