Conversation
|
@codex review |
|
cursor review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ed8253cfb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .optional() | ||
| .describe('Override API base URL for registration relay; defaults to https://x402-worldchain.vercel.app'), | ||
| }), | ||
| description: 'Register this agent with a World ID proof.', |
There was a problem hiding this comment.
Update the root quick start for addressless registration
Update the repository's primary registration instructions alongside this removal of the address argument. README.md:34-48 still directs users to run register <your-wallet-address> and describes Base, manual mode, custom relays, and Base Sepolia, but this command now accepts no address or options and creates its own World Chain identity. Users following the top-level quick start will either receive an unexpected-argument error or, depending on argument handling, register a different generated identity than the wallet they supplied.
Useful? React with 👍 / 👎.
| valid: false, | ||
| error: `Signature verification error: ${reason}. The SIWE message the server reconstructed from your payload:\n\n${message}`, | ||
| } | ||
| body = new Uint8Array(await request.clone().arrayBuffer()) |
There was a problem hiding this comment.
nit. I wonder if there's a way to absorb the request body into the sha input blocks without an additional copy in memory
| type VerifyFunction = (request: Request) => Promise<string> | ||
| type RecoverAddressFunction = (body: Uint8Array, signature: Hex) => Promise<string> | ||
|
|
||
| export function createAgentkitHooksInternal( |
There was a problem hiding this comment.
nit. internal function exported?
| args: [signer.address], | ||
| }) | ||
| } catch (err) { | ||
| return c.error({ |
There was a problem hiding this comment.
All of this error handling feels to me like it should be handled within the ClientContract class
Override ajv to v8 (root-hoisted v6 from eslint broke ajv-draft-04 via incur -> @readme/openapi-parser) and build core before x402/cli tests, since the workspace symlink resolves through core's dist/ exports map. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The X-AgentKit bare EIP-191 body signature was a permanent, universal bearer credential (no audience, method, path, expiry, or nonce binding) and a cross-protocol signing oracle. Requests are now signed under a closed RFC 9421 profile covering @method, @authority, @path, @query, and content-digest (RFC 9530), with created/expires/nonce/keyid/tag params, EIP-191 over the signature base, and recovered-signer == keyid. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… protection The hooks now verify against the request's real method and URL through a single core verifyRequest call (dropping the duplicate address recovery), and enforce single-use nonces via restored hasUsedNonce/recordNonce storage methods. The client signs the RFC 9421 signature base and retries with Signature-Input, Signature, and Content-Digest instead of X-AgentKit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
prove now takes <method> <url> [body] and returns the Content-Digest, Signature-Input, and Signature header values instead of a bare EIP-191 body signature, using the shared profile implementation from @worldcoin/agentkit-core. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Single-use signatures via a nonce move to a follow-up PR. Replay of a byte-identical request is bounded by the five-minute created/expires window until then; docs state this explicitly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rfaced addresses verifyRequest now returned the lowercase wire keyid while extractPayer reads the payment payload's from verbatim (usually EIP-55 checksummed), so the pendingDiscounts lookup never matched and discount recovery silently stopped firing. Discount keys now lowercase both sides, and core surfaces EIP-55 checksummed addresses (results, error addresses, lookups), restoring the pre-RFC-9421 observable behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
deriveComponents interpolated the caller's method string raw, so a direct createSignatureHeaders caller could inject extra lines into the base being signed. Verification was never spoofable (the verifier rebuilds a fixed six-line base from a real Request) and existing callers were guarded upstream, but core is the public signing API, so the letters-only check now lives at the shared choke point. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Body normalization ran before the method check, so a bodyless GET carrying Content-Type: application/json (a common HTTP-client default) passed undefined into normalizeAgentkitJsonBody and was rejected before verification. Determine the method first, skip body retrieval for GET/HEAD, and treat a missing adapter body as the signed empty body — matching what clients sign. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five seconds only covered well-synced clocks; laptops after sleep, VMs, and containers without NTP are routinely seconds fast and would fail every request with SIGNATURE_NOT_YET_VALID. Slow clocks were already tolerated for up to 300 seconds via the age check, so this only evens out the asymmetry and extends the worst-case replay window marginally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolves conflicts between the RFC 9421 signature work and the lookupId rename + lookup cache from new-cli: - verify.ts keeps the RFC 9421 verification flow and adopts the lookupNullifierHash -> lookupId rename plus the 60s lookup cache - VerifiedAgentRequest.nullifierHash renamed to lookupId - RFC 9421 headers (Signature-Input, Signature, Content-Digest) supersede the AgentKit header rename from new-cli - docs and skills keep the RFC 9421 flow with lookup ID terminology Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign requests with RFC 9421 HTTP Message Signatures
Simplify the AgentKit SDK & CLI
Context (how things worked before)
CLI
Previously, the CLI provided a single "register" command, which accepted an ETH address and would register it with the AgentBook contract on the provided chain. It either printed out the transaction details, or used an API endpoint to register. The API address was customizable as a flag.
Core
When building the Shopify integration, I did a quick refactor to split the AgentKit SDK into two packages:
coreandx402. This was done pretty quickly to get the integration done in time, and mostly just exposed an escape hatch to verify x402-shaped signatures without having to pull in their entire SDK.x402
The
x402package contained all the x402-specific parts remaining after the core split. It implements a middleware that checks requests for agentkit data and, if present, applies a configurable discount or free trial.How things work now
CLI
The CLI now manages your agent keys for you. It gets saved in your config directory and used for all calls.
There are now two commands. The first one,
register, requests a World ID proof from the user and registers the agent's pKey on the AgentBook. Future calls do nothing.The
prove {req}command receives a request body, and returns the signed body back to the caller. It errors if the pKey is not registered in the AgentBook, prompting the agent to verify it.Both of these commands are intended to be called by agents, although humans can also use them.
Core
The core package has been pretty much completely rewritten. It now exposes a
verify(request)function that checks that the given request:If those checks succeed, it returns the nullifier hash of the human that the agent acts on behalf of. Otherwise it throws.
x402
The x402 package remains mostly unchanged, just switched to using the new signing implementation in
core.