{"_id":"@accord-protocol/buyer-policy","_rev":"3-58dec7d7ba1940cd2dae99eaf3b01023","name":"@accord-protocol/buyer-policy","dist-tags":{"latest":"0.4.2"},"versions":{"0.4.0":{"name":"@accord-protocol/buyer-policy","version":"0.4.0","keywords":["accord-protocol","agent-payments","policy-engine","spend-limits","agentic-wallet","buyer-policy"],"author":{"name":"bez111"},"license":"MIT","_id":"@accord-protocol/buyer-policy@0.4.0","maintainers":[{"name":"accord-protocol","email":"alexander.bezkrovny@gmail.com"}],"homepage":"https://github.com/accord-protocol/accord-protocol/tree/main/packages/accord-buyer-policy","bugs":{"url":"https://github.com/accord-protocol/accord-protocol/issues"},"dist":{"shasum":"3db65fee97cc34b20285aee4d6d43c7e7cedaf2e","tarball":"https://registry.npmjs.org/@accord-protocol/buyer-policy/-/buyer-policy-0.4.0.tgz","fileCount":6,"integrity":"sha512-cX0FrJ145Xmj40s6tZBlcIP9wTSrh0tj04uj9x+AZ18WpkrpaGSN+H6RZMR1ZhnzSqI4FrUpxTqoe9t/pIT2Jw==","signatures":[{"sig":"MEUCIB3j1eXoByQHDzUptrfzThGdKLHmgtapLhUwoD+Zw4qhAiEAw9wY1ALiDXtFrxGglgJt8TUWZ1hZjFzhci5ybNRR3V0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":67604},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"3de0b386d2e83302cac87206d198a8a799b6baa8","scripts":{"test":"npx tsx --test src/__tests__/*.test.ts","build":"tsup src/index.ts --format esm,cjs --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"accord-protocol","email":"alexander.bezkrovny@gmail.com"},"repository":{"url":"git+https://github.com/accord-protocol/accord-protocol.git","type":"git","directory":"packages/accord-buyer-policy"},"_npmVersion":"10.9.4","description":"Accord Protocol buyer-side policy enforcer. Wraps a buyer agent's signer with hard-coded spend limits, recipient and rail allow-lists, approval-required thresholds and atomic per-session budget tracking. Zero runtime deps. Designed for agentic wallets tha","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@accord-protocol/core":"^0.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","tsup":"^8.0.0","typescript":"^5.0.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/buyer-policy_0.4.0_1778775663401_0.9039417019094007","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@accord-protocol/buyer-policy","version":"0.4.1","keywords":["accord-protocol","agent-payments","policy-engine","spend-limits","agentic-wallet","buyer-policy"],"author":{"name":"bez111"},"license":"MIT","_id":"@accord-protocol/buyer-policy@0.4.1","maintainers":[{"name":"accord-protocol","email":"alexander.bezkrovny@gmail.com"}],"homepage":"https://github.com/accord-protocol/accord-protocol/tree/main/packages/accord-buyer-policy","bugs":{"url":"https://github.com/accord-protocol/accord-protocol/issues"},"dist":{"shasum":"7e901f9ef8fc97b7335477b24ed5c8a924c77147","tarball":"https://registry.npmjs.org/@accord-protocol/buyer-policy/-/buyer-policy-0.4.1.tgz","fileCount":6,"integrity":"sha512-ho7FdBA7OVeR6+JkXG0vktCRmEGWTBYJzyqYn9z3+HXU3jlxs+OHkIVyIfb54rgFePbdiPgZXIpfRwwhfjWDzA==","signatures":[{"sig":"MEYCIQC1yoylTUgdncUSRxkFNTb3ZPr/31sWHJ+VH+jbSkc/MwIhAO5fL1msI7iKXP69Q2ySYt/az6PgtgQWeolJASBfLcIn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@accord-protocol%2fbuyer-policy@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":67612},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"e42de427235ef205df05351fb51e607b159d23b6","scripts":{"test":"npx tsx --test src/__tests__/*.test.ts","build":"tsup src/index.ts --format esm,cjs --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:078b6ce9-da48-4fa5-b179-59651d148dd5"}},"repository":{"url":"git+https://github.com/accord-protocol/accord-protocol.git","type":"git","directory":"packages/accord-buyer-policy"},"_npmVersion":"11.14.1","description":"Accord Protocol buyer-side policy enforcer. Wraps a buyer agent's signer with hard-coded spend limits, recipient and rail allow-lists, approval-required thresholds and atomic per-session budget tracking. Zero runtime deps. Designed for agentic wallets tha","directories":{},"_nodeVersion":"24.15.0","dependencies":{"@accord-protocol/core":"^0.4.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","tsup":"^8.0.0","typescript":"^5.0.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/buyer-policy_0.4.1_1778872879350_0.9032101382736417","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@accord-protocol/buyer-policy","version":"0.4.2","description":"Accord Protocol buyer-side policy enforcer. Wraps a buyer agent's signer with hard-coded spend limits, recipient and rail allow-lists, approval-required thresholds and atomic per-session budget tracking. Zero runtime deps. Designed for agentic wallets tha","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts --clean","typecheck":"tsc --noEmit","test":"npx tsx --test src/__tests__/*.test.ts"},"keywords":["accord-protocol","agent-payments","policy-engine","spend-limits","agentic-wallet","buyer-policy"],"author":{"name":"bez111"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/accord-protocol/accord-protocol.git","directory":"packages/accord-buyer-policy"},"homepage":"https://accordprotocol.ai/learn/buyer-policy-agent-wallets/","bugs":{"url":"https://github.com/accord-protocol/accord-protocol/issues"},"dependencies":{"@accord-protocol/core":"^0.4.2"},"devDependencies":{"@types/node":"^20.0.0","tsup":"^8.0.0","tsx":"^4.0.0","typescript":"^5.0.0"},"engines":{"node":">=18"},"publishConfig":{"access":"public"},"gitHead":"437febb0a70b3162d4b10b19874f1a0026a59bdf","_id":"@accord-protocol/buyer-policy@0.4.2","_nodeVersion":"24.15.0","_npmVersion":"11.15.0","dist":{"integrity":"sha512-5dcXq2vpDHNcKqckd8H/tN5wpw4XpoIED1XRLwafSUVt+nOyhWNvSl1U13Uh7XVL3RlPPnU60qMN7Bb/0XDAzw==","shasum":"2623240571a5cc1fafe3d1030302e8dac6dc66f8","tarball":"https://registry.npmjs.org/@accord-protocol/buyer-policy/-/buyer-policy-0.4.2.tgz","fileCount":6,"unpackedSize":67790,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@accord-protocol%2fbuyer-policy@0.4.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIESIXLmGOD7a8S9PmF+m99Ux01BUfQAPMHNGX/iCGfBHAiAKpucnb19+NjPXefhlvUKfVltsffnzNLbfXUwgc/ZYFg=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:078b6ce9-da48-4fa5-b179-59651d148dd5"}},"directories":{},"maintainers":[{"name":"accord-protocol","email":"alexander.bezkrovny@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/buyer-policy_0.4.2_1779582027970_0.7994430279027553"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-14T16:21:03.236Z","modified":"2026-05-24T00:20:28.435Z","0.4.0":"2026-05-14T16:21:03.676Z","0.4.1":"2026-05-15T19:21:19.481Z","0.4.2":"2026-05-24T00:20:28.132Z"},"bugs":{"url":"https://github.com/accord-protocol/accord-protocol/issues"},"author":{"name":"bez111"},"license":"MIT","homepage":"https://accordprotocol.ai/learn/buyer-policy-agent-wallets/","keywords":["accord-protocol","agent-payments","policy-engine","spend-limits","agentic-wallet","buyer-policy"],"repository":{"type":"git","url":"git+https://github.com/accord-protocol/accord-protocol.git","directory":"packages/accord-buyer-policy"},"description":"Accord Protocol buyer-side policy enforcer. Wraps a buyer agent's signer with hard-coded spend limits, recipient and rail allow-lists, approval-required thresholds and atomic per-session budget tracking. Zero runtime deps. Designed for agentic wallets tha","maintainers":[{"name":"accord-protocol","email":"alexander.bezkrovny@gmail.com"}],"readme":"# `@accord-protocol/buyer-policy`\n\nPolicy enforcer for buyer-side agents. Wraps an integrator-supplied signer with hard-coded spend limits, recipient and rail allow-lists, an approval-required threshold, and atomic per-session budget tracking.\n\nDesigned to be embedded in agentic wallets, autonomous trading desks, or any buyer-side gateway where an LLM-driven or automated agent issues payments on a human's behalf and you need a small, auditable layer that says \"no\" before the signer is even touched.\n\nPublic docs: [Website](https://accordprotocol.ai/) · [Buyer policy for agent wallets](https://accordprotocol.ai/learn/buyer-policy-agent-wallets/) · [Security posture](https://accordprotocol.ai/security/)\n\n## Why this exists\n\nModern agent stacks let an LLM (or any automation) decide when to pay. Without policy, the agent is bounded only by the signer's behaviour — which usually means \"signs anything.\" That is unacceptable for production. This package bounds the blast radius of agent autonomy without taking custody of the key.\n\nSpecifically, it enforces these rules **before** the signer runs:\n\n| Rule | Behaviour |\n|---|---|\n| `maxSinglePayment` | Hard ceiling on any single agreement's price. Even an approved request cannot exceed this. |\n| `maxSessionSpend` | Cumulative cap per session. |\n| `maxDailySpend` *(optional)* | Rolling 24h cap, tracked per session. |\n| `requireApprovalAbove` *(optional)* | Threshold above which the integrator-supplied `approvalHandler` is consulted. Hard timeout. |\n| `allowedRecipients` | Allow-list of `agreement.seller.id` values. Suffix wildcards (`provider://repo-audit-*`) supported. |\n| `allowedRails` | Allow-list of rails (`ergo`, `rosen`, `base`, `x402`). |\n\n## Install\n\n```bash\nnpm install @accord-protocol/buyer-policy @accord-protocol/core\n```\n\n## Quick start\n\n```ts\nimport { createBuyerPolicyEnforcer } from \"@accord-protocol/buyer-policy\";\n\nconst enforcer = createBuyerPolicyEnforcer({\n  policy: {\n    maxSinglePayment:      { amount: \"5\",  currency: \"USD\", decimals: 2 },\n    maxSessionSpend:       { amount: \"50\", currency: \"USD\", decimals: 2 },\n    requireApprovalAbove:  { amount: \"2\",  currency: \"USD\", decimals: 2 },\n    allowedRecipients:     [\"provider://repo-audit-v1\", \"provider://summarizer-*\"],\n    allowedRails:          [\"ergo\", \"x402\"],\n  },\n  signer: async (unsignedTx, { session_id, agreement_id }) => {\n    // Your signer. Receives the unsigned tx for the rail and a context.\n    // Never receives the policy state, never receives the private key from us\n    // (you already own it).\n    return await myWallet.sign(unsignedTx);\n  },\n  approvalHandler: async (request, abortSignal) => {\n    // Push a notification, wait for a tap, return the verdict.\n    // Honour abortSignal — the enforcer aborts after `approvalTimeoutMs`.\n    return await pushAndAwait(request, { signal: abortSignal });\n  },\n});\n\nconst session = enforcer.openSession({ agentId: \"agent://atlas-trader\" });\n\nconst { signedTx, sessionSpend } = await session.authorize({\n  agreement,\n  rail: \"ergo\",\n  unsignedTx,\n});\n```\n\n## Threat model\n\nThis package is the small, paranoid layer between your agent and your signer. The defences in scope:\n\n| Threat | Mitigation |\n|---|---|\n| Time-of-check / time-of-use across concurrent `authorize()` calls | Per-session `AsyncMutex`. Budget is incremented BEFORE the signer is invoked, rolled back if the signer rejects. |\n| JS Number precision drift around caps | Every amount is parsed from a decimal string into a BigInt scaled by `decimals`. JS numbers are rejected at the API boundary. |\n| Cross-currency comparison | All caps share one `(currency, decimals)`. Mismatched agreement currency rejects with `CURRENCY_MISMATCH`; converting belongs in an oracle layer the integrator wires up. |\n| Allow-list bypass via wildcard pattern | Only suffix `*` is honoured. Mid-string or leading wildcards reject at construction. |\n| Approval handler hang | `AbortController` + hard timeout (default 60s). Handler exceptions surface as `APPROVAL_HANDLER_ERROR`. |\n| Session-id forgery | IDs are 16 random bytes from `crypto.randomBytes`, hex-encoded. Membership lookup uses `timingSafeEqual`. |\n| Information leak via error messages | Errors carry typed `code` strings; messages reference field names only — never amount values, agreement bodies, or signer payloads. |\n| Mutable policy mid-flight | Parsed policy is frozen at construction. Subsequent edits to the input policy object have no effect. |\n| Approval forgery via in-process callback | `approvalHandler` receives only the public-facing facts a human needs. Wiring it to an authenticated channel (push, signed token, separate process) is the integrator's responsibility — but the handler does NOT receive the unsigned tx, the signer state, or any way to influence them beyond `{approved: boolean}`. |\n\nWhat this package does **not** do (intentionally):\n\n- It does **not** store private keys. Your `signer` function owns the key.\n- It does **not** generate or rotate keys.\n- It does **not** implement push notifications. You wire those into `approvalHandler`.\n- It does **not** persist sessions across processes. In-memory only; if you need durability, build a `SessionStore` in front of `openSession()`.\n- It does **not** isolate against malicious code in the same process. If an attacker can `import` this module's internals they can already reach the signer too. Run untrusted code in a separate process with this package on the trusted side.\n\n## Error codes\n\nAll deny paths surface as `BuyerPolicyError` with one of these codes. Branch on `err.code`, not on `err.message`.\n\n```text\nPOLICY_INVALID_CONFIG               – misconfigured policy at construction\nPOLICY_INVALID_AMOUNT_FORMAT        – non-string amount, bad decimals, etc.\nPOLICY_INVALID_RECIPIENT_PATTERN    – wildcard in wrong place, too long, etc.\nAGREEMENT_INVALID                   – schema validation failed\nRAIL_NOT_ALLOWED                    – rail outside allowedRails\nRECIPIENT_NOT_ALLOWED               – seller.id outside allowedRecipients\nCURRENCY_MISMATCH                   – agreement currency / decimals don't match policy\nBUDGET_EXCEEDED_SINGLE              – price > maxSinglePayment\nBUDGET_EXCEEDED_SESSION             – spent + price > maxSessionSpend\nBUDGET_EXCEEDED_DAILY               – 24h rolling sum + price > maxDailySpend\nAPPROVAL_REQUIRED_NO_HANDLER        – above threshold but no handler registered\nAPPROVAL_DENIED                     – handler returned approved: false\nAPPROVAL_TIMEOUT                    – handler did not return within timeout\nAPPROVAL_HANDLER_ERROR              – handler threw or returned malformed verdict\nSESSION_EXPIRED                     – sessionTtlMs elapsed\nSESSION_CLOSED                      – session.close() was called\nSIGNER_ERROR                        – signer rejected; budget rolled back\n```\n\n## Status\n\nAlpha at v0.4.2. The API is intended to remain stable through the v0.x line; breaking changes will land in a v1 release. Conformance with this package is **not** a registry-level claim — it is a buyer-side hygiene tool.\n\nThe package is **not** a substitute for an external audit of the rest of the Accord stack. Mainnet trees and contracts are still gated by the audit manifest workflow described in [ACCORD-010](../../specs/ACCORD-010-security-audit.md).\n","readmeFilename":"README.md"}