Skip to content

feat!: agentkit cli v0.2 - #38

Open
m1guelpf wants to merge 19 commits into
mainfrom
new-cli
Open

feat!: agentkit cli v0.2#38
m1guelpf wants to merge 19 commits into
mainfrom
new-cli

Conversation

@m1guelpf

@m1guelpf m1guelpf commented Aug 11, 2026

Copy link
Copy Markdown
Member

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: core and x402. 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 x402 package 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:

  • has the agentkit header
  • the agentkit header contains a valid signature of the request's body
  • the pKey that signed the body is in the agent book

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.

@paolodamico

Copy link
Copy Markdown
Contributor

@codex review

@paolodamico

Copy link
Copy Markdown
Contributor

cursor review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread cli/src/prove.ts Outdated
Comment thread cli/src/index.ts
.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.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread cli/src/prove.ts Outdated
@paolodamico paolodamico changed the title new cli feat!: agentkit cli v0.2 Aug 17, 2026
Comment thread core/src/verify.ts Outdated
Comment thread core/src/verify.ts Outdated
Comment thread core/src/verify.ts Outdated
Comment thread core/src/verify.ts Outdated
Comment thread core/src/verify.ts
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())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. I wonder if there's a way to absorb the request body into the sha input blocks without an additional copy in memory

Comment thread cli/REGISTRATION.md Outdated
Comment thread x402/src/storage.ts
Comment thread x402/src/hooks.ts
type VerifyFunction = (request: Request) => Promise<string>
type RecoverAddressFunction = (body: Uint8Array, signature: Hex) => Promise<string>

export function createAgentkitHooksInternal(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. internal function exported?

Comment thread README.md Outdated
Comment thread x402/DOCS.md Outdated

@thomas-waite thomas-waite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, think this is a really good step. It makes it much simpler to use, with the key/crypto stuff being abstracted away.

Main thing to discuss is the signature scheme, and also thinking about renaming the x402 server component

Comment thread cli/src/key.ts Outdated
Comment thread cli/src/key.ts Outdated
Comment thread cli/src/index.ts
args: [signer.address],
})
} catch (err) {
return c.error({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this error handling feels to me like it should be handled within the ClientContract class

Comment thread cli/test/key.test.ts
m1guelpf and others added 12 commits August 24, 2026 14:47
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>
m1guelpf and others added 7 commits August 27, 2026 04:35
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants