{"_id":"@agenttool/credential-broker","_rev":"2-3d602543829ad7bd1045450573528a2e","name":"@agenttool/credential-broker","dist-tags":{"latest":"0.3.1"},"versions":{"0.1.0":{"name":"@agenttool/credential-broker","version":"0.1.0","keywords":["agent","credentials","capabilities","keychain","security","secret-broker","unix-socket"],"license":"Apache-2.0","_id":"@agenttool/credential-broker@0.1.0","maintainers":[{"name":"agenttool","email":"contact@cambridgetcg.com"}],"homepage":"https://docs.agenttool.dev","bugs":{"url":"https://github.com/cambridgetcg/agenttool/issues"},"bin":{"agentcred":"dist/cli.js"},"dist":{"shasum":"264d01ee539c0c45a2ed9a3816ef764c27b0b522","tarball":"https://registry.npmjs.org/@agenttool/credential-broker/-/credential-broker-0.1.0.tgz","fileCount":65,"integrity":"sha512-lQq6PpyhuwV/uUyyhujrQCpMsFfHAjodOgU+B2PFhmuo/b22F4CNaWPnLcIMn3s0Zmp4WlgBWfciil6gZa6klw==","signatures":[{"sig":"MEUCIHSWea6hQhwF4tJctbRv3HAeoky56bRjhlu3NksfGfYqAiEAuLVC51nFG/jYnOwwwVcwfvRlOGRgPQMlFWqQhqmyGpk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agenttool%2fcredential-broker@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":288473},"main":"dist/index.js","type":"module","_from":"file:apps/docs/packages/v1/@agenttool/credential-broker/0.1.0/agenttool-credential-broker-0.1.0.tgz","types":"dist/index.d.ts","engines":{"bun":">=1.3.5","node":">=20.19.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js"}},"scripts":{"ci":"bun run typecheck && bun run build && bun test","test":"bun test","build":"bun run clean && tsc","clean":"node --eval \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"","prepack":"bun run ci","typecheck":"tsc --noEmit","test:security":"bun test tests","prepublishOnly":"bun run ci"},"_npmUser":{"name":"agenttool","email":"contact@cambridgetcg.com"},"_resolved":"/home/runner/work/agenttool/agenttool/apps/docs/packages/v1/@agenttool/credential-broker/0.1.0/agenttool-credential-broker-0.1.0.tgz","_integrity":"sha512-lQq6PpyhuwV/uUyyhujrQCpMsFfHAjodOgU+B2PFhmuo/b22F4CNaWPnLcIMn3s0Zmp4WlgBWfciil6gZa6klw==","repository":{"url":"git+https://github.com/cambridgetcg/agenttool.git","type":"git","directory":"packages/credential-broker"},"_npmVersion":"11.17.0","description":"Local capability broker for agents to use credentials without receiving secret values","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.2.0","typescript":"^5.7.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/credential-broker_0.1.0_1784670661482_0.9021001106779711","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@agenttool/credential-broker","version":"0.3.1","description":"Local capability broker for agents to use credentials without receiving secret values","license":"Apache-2.0","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js"}},"bin":{"agentcred":"dist/cli.js","agentcred-control":"dist/controller-cli.js"},"publishConfig":{"access":"public"},"scripts":{"clean":"node --eval \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"","build":"bun run clean && tsc","typecheck":"tsc --noEmit","test":"bun test","test:security":"bun test tests","ci":"bun run typecheck && bun run build && bun test","prepack":"bun run ci","prepublishOnly":"bun run ci"},"keywords":["agent","credentials","capabilities","keychain","security","secret-broker","unix-socket"],"engines":{"node":">=20.19.0","bun":">=1.3.5"},"devDependencies":{"@types/bun":"^1.2.0","@types/node":"^22.0.0","typescript":"^5.7.0"},"repository":{"type":"git","url":"git+https://github.com/cambridgetcg/agenttool.git","directory":"packages/credential-broker"},"homepage":"https://docs.agenttool.dev","_id":"@agenttool/credential-broker@0.3.1","bugs":{"url":"https://github.com/cambridgetcg/agenttool/issues"},"_integrity":"sha512-mzR1xyjPr4tVU7JeKselSpz1YWYDLBg/GmnubYZ23fTOQLPiv9pyR/RiMOyaawCiri8MJX15bXobfneA+dDX2Q==","_resolved":"/home/runner/work/_temp/agenttool-npm-release/agenttool-credential-broker-0.3.1.tgz","_from":"file:/home/runner/work/_temp/agenttool-npm-release/agenttool-credential-broker-0.3.1.tgz","_nodeVersion":"24.18.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-mzR1xyjPr4tVU7JeKselSpz1YWYDLBg/GmnubYZ23fTOQLPiv9pyR/RiMOyaawCiri8MJX15bXobfneA+dDX2Q==","shasum":"c989e37eed883912a422cb17b89986d7f0643858","tarball":"https://registry.npmjs.org/@agenttool/credential-broker/-/credential-broker-0.3.1.tgz","fileCount":103,"unpackedSize":741046,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agenttool%2fcredential-broker@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIC+9/aTT9dhj/eeaG6j/fwnDN5sfBpFzkkgfEzv6IhKUAiB3B2xWPFrq3l7nP6XbZ/D4FnTEvSdcQqMjZg+IWxEy+Q=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b29550d7-e346-4402-b338-1be9e65ea3c6"}},"directories":{},"maintainers":[{"name":"agenttool","email":"contact@cambridgetcg.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/credential-broker_0.3.1_1785360806119_0.9758971754623198"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-21T21:51:01.301Z","modified":"2026-07-29T21:33:26.709Z","0.1.0":"2026-07-21T21:51:01.638Z","0.3.1":"2026-07-29T21:33:26.360Z"},"bugs":{"url":"https://github.com/cambridgetcg/agenttool/issues"},"license":"Apache-2.0","homepage":"https://docs.agenttool.dev","keywords":["agent","credentials","capabilities","keychain","security","secret-broker","unix-socket"],"repository":{"type":"git","url":"git+https://github.com/cambridgetcg/agenttool.git","directory":"packages/credential-broker"},"description":"Local capability broker for agents to use credentials without receiving secret values","maintainers":[{"name":"agenttool","email":"contact@cambridgetcg.com"}],"readme":"# `@agenttool/credential-broker`\n\nLocal, capability-scoped credential use for agent runtimes.\n\nThe broker gives an SDK permission to perform a bounded operation. It does not\ngive the SDK, model, or chat a credential value. The design is deliberately\ncloser to `ssh-agent` than to environment-variable injection.\n\n```text\nhuman-owned config / consent\n             |\nOS vault -> local broker --------> approved HTTPS origin\n                ^\n                | owner-only Unix socket\n          agent SDK (opaque grant handle)\n```\n\n`agentcred/0.1` is an experimental protocol and this package is a developer\npreview. Read [SPEC.md](./SPEC.md) and the limitations below before using it\nwith a valuable credential. The separately negotiated\n[`agentcred.evm-jsonrpc-read/0.1`](./JSONRPC-READ-0.1.md) profile adds a\nmethod-aware EVM read surface without widening generic `http.fetch`.\n\nThis source tree describes the `0.3.1` release line. Check an immutable LOVE\nmanifest or exact npm version before treating any distribution mirror as\navailable. Source, a branch, or a mutable registry tag is not release\nevidence.\n\n## What the preview does\n\n- exposes no `getSecret`, reveal, export, or credential-list operation;\n- keeps capability strings out of the public `GrantHandle` and its JSON form;\n- binds grants to one socket connection, a monotonic TTL, and an atomic use\n  count;\n- restricts requests to an exact HTTPS origin, methods, and canonical path\n  prefixes, deny-by-default query names, and exact values for\n  authority-sensitive headers;\n- keeps caller-supplied x402 `PAYMENT-SIGNATURE` headers denied unless both\n  owner policy and the individual grant explicitly opt in;\n- validates every DNS answer and pins a validated address into the TLS\n  connection without using a shared connection pool;\n- refuses redirects, private/reserved destinations unless both owner policy\n  and grant explicitly opt in, caller authentication headers, hop-by-hop\n  headers, and compressed responses;\n- bounds request and response bodies and removes exact secret-byte reflections\n  before results, errors, and metadata audits cross the broker boundary;\n- can negotiate a closed EVM JSON-RPC read profile whose client supplies only\n  a CAIP-2 chain ID, one allowlisted method, and method-specific params; the\n  broker owns the exact origin plus `/v2`, envelope, ID, headers, and Bearer\n  credential injection;\n- negotiates client concurrency and bounds per-session/global in-flight work\n  and active grants; and\n- latches audit failure, denying new grants and uses by default.\n\n## What it does not do\n\n- The portable Node server cannot inspect `SO_PEERCRED`, `getpeereid`, macOS\n  audit tokens, or executable code identity. Socket permissions provide a\n  same-user filesystem boundary, not proof of the calling program. Supply a\n  native `authorizePeer` hook that returns an OS-observed `PeerIdentity` for a\n  stronger deployment; the broker passes it to consent and metadata audit.\n- The included CLI uses an owner-authored standing policy; it has no trusted\n  per-use consent window yet. A host app can supply its own `ConsentProvider`.\n- `agentcred-control` stores and changes only local Keychain/manifest state.\n  It does not issue or revoke provider credentials. Its drain and provider\n  revocation evidence IDs are explicit human attestations, not remote proof.\n  The lifecycle lock coordinates cooperating broker/controller processes; it\n  is not a same-user security boundary.\n- Its TTY check blocks accidental pipes; it does not authenticate a human,\n  record consent, or stop a same-user process from allocating a pseudo-TTY.\n- A managed generation ID identifies one random Keychain slot reference chosen\n  at broker startup. It is not a hash or attestation of secret bytes. An\n  out-of-band same-user update to that Keychain item is not detected.\n- Rotation audit verification checks an exact alias, slot generation, HTTPS\n  origin, GET/HEAD path hash, method, timestamp, and exact success/revoked\n  status. It proves only that bounded broker observation. It is authentication\n  evidence only when the owner chose an endpoint whose documented semantics\n  require that credential. No provider administration adapter ships yet.\n- The macOS adapter invokes the fixed `/usr/bin/security` binary. Secret bytes\n  pass through that subprocess and broker memory. This is not equivalent to a\n  code-signed native Security.framework helper with a broker-only Keychain ACL.\n- HTTP credential values must be non-empty printable ASCII bytes (`0x20`–\n  `0x7e`). Binary and non-ASCII values are rejected before injection so Node's\n  header wire bytes remain identical to the bytes searched by exact-response\n  redaction.\n- A process with unrestricted access as the same macOS user may be able to\n  inspect or invoke the same Keychain item independently. Root, a compromised\n  broker, an approved malicious upstream, and a malicious approved executable\n  are outside this preview's protection.\n- Redaction guarantees exact-byte removal only. An upstream can transform,\n  encode, split, encrypt, or infer data in ways a generic redactor cannot\n  identify.\n- Responses are buffered and limited to 32 KiB. SSE and other long-lived\n  streaming APIs are rejected before a use is reserved in `0.1`. In AgentTool,\n  this means `wake.voice`, `strands.thoughts.voice`, and `inbox.voice` are not\n  available through this broker version.\n- The EVM JSON-RPC extension supports seven small read methods only. It does\n  not expose arbitrary RPC, logs, traces, simulation, subscriptions,\n  transaction broadcast, prepared wallet calls, or Alchemy administration.\n  Its origin-to-chain association is owner-configured; only an explicit\n  `eth_chainId` call live-checks that association.\n- The broker does not create, verify, decode, or place a spending limit on an\n  x402 `PAYMENT-SIGNATURE`. Enabling `allowPaymentSignature` only forwards a\n  caller-supplied signature within the origin/method/path/use boundary. Prefer\n  a fresh, short-lived, one-use grant for one exact paid tool path and a\n  trusted consent surface that checks the payment terms before signing.\n- Aborting caller-side `fetch` rejects locally, but does not recall an operation\n  already dispatched to the broker or undo an upstream side effect.\n  `callEvmJsonRpcRead()` has no per-use abort signal in this preview: its local\n  timeout stops waiting, while closing the session propagates cancellation to\n  in-flight broker work.\n- The JSONL audit stops at 10 MiB rather than rotating. The server emits one\n  safe operator notification and denies subsequent grants/uses by default;\n  deploy a managed `AuditSink` for rotation or tamper evidence.\n- Only a macOS Keychain source is included. Linux Secret Service, Windows\n  Credential Manager, native user-presence UI, and non-exporting signing are\n  planned adapters.\n\nIn short: this preview keeps bearer values out of normal model/chat/SDK state\nand materially narrows their use. It is not an absolute same-user sandbox.\n\n## Install for development\n\n```sh\ncd packages/credential-broker\nbun install\nbun run ci\n```\n\nNo runtime npm dependencies are used.\nThe hermetic TLS tests invoke a local `openssl` binary to generate and remove\nephemeral test-only certificates; no private-key fixture is stored.\n\nCredential aliases are 1–128 ASCII characters:\n`[A-Za-z0-9_][A-Za-z0-9._:/@+-]{0,127}`. This 0.3 preview intentionally\ntightens 0.2's general trimmed-string acceptance. Rename any existing alias\nwith spaces, other characters, or 129–256 characters before upgrading.\n\n## Run the local broker on macOS\n\n### Standard human-to-broker handoff\n\nUse the separate `agentcred-control` executable for new integrations. It\ncreates an owner-only, value-free A/B manifest and lets the fixed macOS\n`security` tool receive the value from an interactive prompt. The value is\nnever a controller argument, environment variable, pipe, JSON field, receipt,\nor agent-wire message. The interactive-TTY check is an anti-pipe guard, not\nhuman authentication or a consent record.\n\nInitialize one logical credential and one exact read-only verification probe.\nThe path must be canonical and query-free; lifecycle probes support HTTPS\n`GET`/`HEAD` only. The verification origin/path are persisted non-secret\nmetadata: never embed a key in either, because argv and the manifest would\nexpose it.\n\n```sh\nmkdir -p ~/.config/agentcred/credentials\nchmod 700 ~/.config/agentcred ~/.config/agentcred/credentials\n\nagentcred-control init \\\n  --manifest \"$HOME/.config/agentcred/credentials/agenttool.json\" \\\n  --credential agenttool/default \\\n  --provider agenttool \\\n  --purpose bounded-api \\\n  --environment local \\\n  --account \"$USER\" \\\n  --auth bearer \\\n  --verify-operation http.fetch \\\n  --verify-origin https://api.example.com \\\n  --verify-path /v1/whoami \\\n  --verify-method GET \\\n  --verify-success-status 200 \\\n  --verify-revoked-status 401\n```\n\nBroker config maps the normal, candidate, and previous verification aliases\nto the same manifest. The broker resolves these aliases once at startup; it\ndoes not follow a live pointer change:\n\n```json\n{\n  \"credentials\": {\n    \"agenttool/default\": {\n      \"backend\": \"managed-macos-keychain\",\n      \"manifestPath\": \"/Users/you/.config/agentcred/credentials/agenttool.json\",\n      \"selection\": \"active\",\n      \"auth\": { \"kind\": \"bearer\" }\n    },\n    \"agenttool/default/candidate\": {\n      \"backend\": \"managed-macos-keychain\",\n      \"manifestPath\": \"/Users/you/.config/agentcred/credentials/agenttool.json\",\n      \"selection\": \"candidate\",\n      \"auth\": { \"kind\": \"bearer\" }\n    },\n    \"agenttool/default/previous\": {\n      \"backend\": \"managed-macos-keychain\",\n      \"manifestPath\": \"/Users/you/.config/agentcred/credentials/agenttool.json\",\n      \"selection\": \"previous\",\n      \"auth\": { \"kind\": \"bearer\" }\n    }\n  }\n}\n```\n\nAdd exact, read-only policies for the candidate and previous aliases before\nusing them as verification probes. Do not put a credential value in any\nprovider/evidence ID. With the broker stopped, stage the provider-issued value\nfrom your own Terminal:\n\n```sh\nagentcred-control stage \\\n  --config \"$HOME/.config/agentcred/config.json\" \\\n  --credential agenttool/default/candidate\n```\n\nIf the prompt or process ends ambiguously, the manifest stays in\n`provisioning`. `recover-stage` is presence-only: it advances when the exact\ncommitted Keychain item already exists and otherwise requires provider cleanup.\nIf the native prompt was cancelled before that item was created and the same\nprovider-issued value is still the intended candidate, explicitly reopen the\nfixed prompt with:\n\n```sh\nagentcred-control resume-stage \\\n  --config \"$HOME/.config/agentcred/config.json\" \\\n  --credential agenttool/default/candidate\n```\n\n`resume-stage` first inspects the exact committed service/account. It advances\nwithout prompting if that item already exists; if absent, it prompts only\nthrough the fixed macOS Keychain controller. It freshly rechecks expiry and\noverlap immediately after the absent result and before that prompt, confirms\nthe exact item, rechecks again, and then advances. It accepts no value,\nstdin/env source, provider URL, or command. Initial `stage` likewise commits\nthe slot identity first, freshly checks the same time bounds immediately\nbefore prompting, and rechecks after confirmation. Both recovery commands are\nvalid only in `provisioning`. If the intended provider value is no longer\navailable or its identity is uncertain, clean it up at the provider and use\nthe explicit candidate-abort flow instead.\n\nAfter a controller crash or forced termination, do not run `recover-lock` or\n`resume-stage` until you have confirmed that the former native prompt has\nended and no surviving `/usr/bin/security add-generic-password` child from\nthat staging attempt remains. The cooperative lock records the controller\nprocess, not its child. A parent `SIGKILL` or crash can therefore leave an\nuntracked native prompt; this preview does not provide cross-crash child\nsupervision. The fixed committed service/account and omission of `-U` prevent\nthe controller from updating an item that already exists, but they do not\nremove the race between two prompts or prove which value was entered.\n\nStart the broker, perform one harmless authenticated request through the\ncandidate alias, and retain its returned `auditId`. Stop the broker again,\nthen record and activate that evidence within five minutes:\n\n```sh\nagentcred-control verify-new \\\n  --config \"$HOME/.config/agentcred/config.json\" \\\n  --credential agenttool/default/candidate \\\n  --audit-id 00000000-0000-4000-8000-000000000000\n\nagentcred-control activate \\\n  --config \"$HOME/.config/agentcred/config.json\" \\\n  --credential agenttool/default/candidate\n```\n\nUse the real audit ID, never a placeholder. A `2xx` audit event is meaningful\nonly when the owner-selected endpoint actually requires and identifies the\ncredential. The controller does not infer authentication from an arbitrary\npublic endpoint. It binds the observation to a slot generation ID, not to a\nhash of Keychain bytes. Without a provider-returned key ID, mistakenly staging\nthe old value again can pass the positive probe.\n\nEvery existing-manifest lifecycle transition takes the same cooperative lock\nheld by a running broker, so stage/cutover/rollback requires the broker to be\nstopped. Create-only `init` precedes that lifecycle; explicit stale-lock\nrecovery necessarily inspects and removes the lock outside it.\nRestarting snapshots the selected Keychain reference and invalidates the old\nconnection-bound grants. It does not freeze or attest bytes against a\nsame-user out-of-band Keychain update.\n\nRoutine rotation then uses explicit `drain`, `prepare-old-revoke`,\nprovider-side revoke, `attest-old-revoked`, `verify-revoked`, and `close`\nsteps. Before the durable no-rollback boundary, the active slot needs fresh\nexact positive proof and the previous slot needs either fresh exact positive\nproof or the profile's exact revoked status. The latter is a degraded-recovery\npath for an already-dead predecessor, not a generic failure bypass. After the\nremote revoke, a fresh exact old-negative observation remains mandatory;\nactive-positive evidence is recorded when available but cannot strand the\nirreversible cleanup path. Candidate cancellation similarly uses\n`prepare-abort`, provider-side revoke, `attest-candidate-revoked`, and\n`close-abort`; no command performs the provider action.\n\nInspect/recover a stale cooperative lock with `lock-status` followed by\n`recover-lock` using the exact nonce only after the recorded PID is absent and\nyou have confirmed that no native Keychain prompt or surviving\n`/usr/bin/security` child from that controller remains. Lock recovery does not\nperform that child-process check for you. If prompt termination or entered\nvalue identity is ambiguous, do not resume; clean up the provider candidate\nand follow the abort flow.\nAfter eight retained closure receipts, use `archive`, then verify the result\nagainst the live manifest with:\n\n```sh\nagentcred-control verify-archive \\\n  --archive \"$HOME/.config/agentcred/credentials/archive-001.json\" \\\n  --manifest \"$HOME/.config/agentcred/credentials/agenttool.json\"\n```\n\nSee [ROTATION.md](./ROTATION.md) for routine rotation, rollback, emergency\ncontainment, provider-specific boundaries, and every irreversible gate.\n\n### Direct unmanaged reference\n\nFor a simple unmanaged preview, provision a Keychain item yourself outside the\nagent conversation. Putting `-w` last makes the system tool prompt instead of\nplacing the value in process arguments:\n\n```sh\nsecurity add-generic-password \\\n  -U \\\n  -s agenttool-soma-bearer \\\n  -a \"$USER\" \\\n  -w\n```\n\nCreate `~/.config/agentcred/config.json` containing references and policy only,\nnever secret values:\n\n```json\n{\n  \"socketPath\": \"/Users/you/.config/agentcred/run/agentcred.sock\",\n  \"auditPath\": \"/Users/you/.config/agentcred/audit.jsonl\",\n  \"credentials\": {\n    \"agenttool/default\": {\n      \"backend\": \"macos-keychain\",\n      \"service\": \"agenttool-soma-bearer\",\n      \"account\": \"you\",\n      \"auth\": { \"kind\": \"bearer\" }\n    }\n  },\n  \"policies\": [\n    {\n      \"credential\": \"agenttool/default\",\n      \"origin\": \"https://api.agenttool.dev\",\n      \"methods\": [\"GET\", \"POST\", \"PATCH\", \"DELETE\"],\n      \"pathPrefixes\": [\"/v1\"],\n      \"queryNames\": [],\n      \"allowPaymentSignature\": false,\n      \"maxTtlSeconds\": 300,\n      \"maxUses\": 50,\n      \"maxRequestBytes\": 32768,\n      \"maxResponseBytes\": 32768\n    }\n  ]\n}\n```\n\nProtect and validate it, then start the daemon from an owner-controlled local\nsession:\n\n```sh\nchmod 700 ~/.config/agentcred\nchmod 600 ~/.config/agentcred/config.json\nagentcred check --config ~/.config/agentcred/config.json\nagentcred serve --config ~/.config/agentcred/config.json\n```\n\nThe audit is metadata-only and owner-readable. It is not tamper-proof.\nQuery parameters are denied unless both policy and grant list their exact\nnames. Values remain caller-controlled. `X-Agent-Id` is also denied unless\nboth scopes contain an exact value, for example\n`\"headerValues\":{\"x-agent-id\":[\"<approved identity id>\"]}`.\n`PAYMENT-SIGNATURE` is separately denied by default. To support an x402 retry,\nset `\"allowPaymentSignature\": true` in both the owner policy and the requested\ngrant. This permits forwarding only; it does not sign or validate payment\nterms.\n\n### Alchemy EVM read policy\n\nAn Alchemy Chain API key can use the negotiated JSON-RPC profile without\nputting the key in the endpoint URL. The credential mapping must be\n`\"kind\": \"bearer\"`:\n\n```json\n{\n  \"credentials\": {\n    \"alchemy/ethereum-read\": {\n      \"backend\": \"macos-keychain\",\n      \"service\": \"alchemy-ethereum-read\",\n      \"account\": \"you\",\n      \"auth\": { \"kind\": \"bearer\" }\n    }\n  },\n  \"policies\": [\n    {\n      \"operation\": \"jsonrpc.read\",\n      \"profile\": \"agentcred.evm-jsonrpc-read/0.1\",\n      \"credential\": \"alchemy/ethereum-read\",\n      \"origin\": \"https://eth-mainnet.g.alchemy.com\",\n      \"chainId\": \"eip155:1\",\n      \"methods\": [\n        \"eth_chainId\",\n        \"eth_blockNumber\",\n        \"eth_getBalance\",\n        \"eth_getTransactionReceipt\"\n      ],\n      \"maxTtlSeconds\": 120,\n      \"maxUses\": 20,\n      \"maxRequestBytes\": 1024,\n      \"maxResponseBytes\": 4096\n    }\n  ]\n}\n```\n\nThis profile is for agent read operations, not credential lifecycle evidence.\n`agentcred-control` rotation requires an authentication-bound HTTP GET/HEAD\nprobe with exact status semantics; without one or a provider adapter, Alchemy\nrotation remains a manual provider procedure. Legacy Alchemy key-in-URL paths\nmust not be used as `--verify-path`.\n\nThe path is fixed by the profile to `/v2`; there is intentionally no URL,\npath, query, header, raw body, JSON-RPC ID, batch, or notification input.\n\n## Client API\n\n```ts\nimport { AgentCredClient } from \"@agenttool/credential-broker\";\n\nconst broker = new AgentCredClient({\n  socketPath: `${process.env.HOME}/.config/agentcred/run/agentcred.sock`,\n});\nawait broker.connect();\n\nconst grant = await broker.requestGrant({\n  alias: \"agenttool-session\",       // model-safe label, not authority\n  credential: \"agenttool/default\", // owner-configured opaque reference\n  operation: \"http.fetch\",\n  scope: {\n    origin: \"https://api.agenttool.dev\",\n    methods: [\"GET\", \"POST\"],\n    pathPrefixes: [\"/v1\"],\n    queryNames: [],\n    ttlSeconds: 120,\n    maxUses: 20,\n  },\n});\n\nconst brokeredFetch = broker.asFetch(grant);\nconst response = await brokeredFetch(\"https://api.agenttool.dev/v1/wake\");\n\n// AgentTool SDK uses the object-form transport directly:\n// import { AgentTool } from \"@agenttool/sdk\";\n// const at = new AgentTool({ transport: broker.asTransport(grant) });\n\nawait broker.revoke(grant);\nbroker.close();\n```\n\nThe same client offers the shipped JSON-RPC profile during `hello` by default.\nA method-aware Alchemy read is requested separately:\n\n```ts\nimport {\n  AGENTCRED_EVM_JSONRPC_READ_PROFILE,\n  AgentCredClient,\n} from \"@agenttool/credential-broker\";\n\nconst broker = new AgentCredClient({\n  socketPath: `${process.env.HOME}/.config/agentcred/run/agentcred.sock`,\n});\nawait broker.connect();\n\nconst grant = await broker.requestGrant({\n  alias: \"ethereum-observation\",\n  credential: \"alchemy/ethereum-read\",\n  operation: \"jsonrpc.read\",\n  scope: {\n    profile: AGENTCRED_EVM_JSONRPC_READ_PROFILE,\n    origin: \"https://eth-mainnet.g.alchemy.com\",\n    chainId: \"eip155:1\",\n    methods: [\"eth_getBalance\"],\n    ttlSeconds: 60,\n    maxUses: 2,\n    maxRequestBytes: 1024,\n    maxResponseBytes: 4096,\n  },\n});\n\nconst balance = await broker.callEvmJsonRpcRead(grant, {\n  chainId: \"eip155:1\",\n  method: \"eth_getBalance\",\n  params: [\"0x1111111111111111111111111111111111111111\", \"finalized\"],\n});\n\nawait broker.revoke(grant);\nbroker.close();\n```\n\nThe returned handle serializes only its alias and receipt. Application code\nshould keep the client and handle in trusted host state rather than exposing\nthe client object as a model tool.\n\n## Package API\n\n- `BrokerServer`: local Unix-socket broker core.\n- `AgentCredClient`: connection and opaque-handle client.\n- `AgentCredClient.callEvmJsonRpcRead`: negotiated, method-aware EVM reads\n  without a caller-controlled URL or raw JSON-RPC envelope.\n- `MacOSKeychainSource`: broker-only Keychain reader.\n- `managed-macos-keychain` config references: startup-frozen A/B manifest\n  selections for offline handoff and rotation.\n- `agentcred-control`: interactive controller-plane provisioning, explicit\n  prompt resume, presence-only recovery, rotation, and closure-archive CLI.\n  Its TTY check is anti-pipe only; it is not an agent SDK or wire surface.\n- `PolicyConsent`: owner-authored standing allowlist.\n- `ConsentProvider`: hook for a native out-of-band approval UI.\n- `AuditSink`: metadata-only audit hook.\n- `BrokerServerOptions.authorizePeer`: hook for a native peer identity check.\n\n`OutboundTransport` is a trusted broker-internal extension point, not an agent\nplugin. It receives credential-bearing headers and must enforce the validated\npinned address, normal TLS hostname/certificate checks, no redirects, no\ncompression, aborts, timeouts, and response limits. Prefer the included\n`NodeHttpsTransport` unless the replacement can uphold all of those rules.\nIts optional `ca` constructor input exists for hermetic tests and explicitly\nhost-controlled private trust roots. Supplying it replaces Node's default CA\nset, so only trusted broker-host code may set it; never derive it from an\nagent, grant, config field, or wire request. Omitting it preserves the system\ntrust store and certificate verification remains enabled in either mode.\n\nTest-only in-memory credentials and fake clocks live under\n`@agenttool/credential-broker/testing` so they are not mistaken for production\nbackends.\n\n## License\n\nApache-2.0. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).\n","readmeFilename":"README.md"}