Skip to content

Add Relay.keyAuthorization plugin for storing access key authorizations - #5214

Open
jxom wants to merge 3 commits into
mainfrom
jxom/admiring-dirac-wsrnxc
Open

jxom wants to merge 3 commits into
mainfrom
jxom/admiring-dirac-wsrnxc

Conversation

@jxom

@jxom jxom commented Oct 9, 2026

Copy link
Copy Markdown
Member

What is this PR solving?

This PR adds the Relay.keyAuthorization plugin to viem/tempo, which stores signed key authorizations and attaches them to an access key's next transaction. This enables applications to authorize access keys offchain and have them register onchain in a single transaction.

The plugin:

  • Saves key authorizations signed with Actions.accessKey.signAuthorization via the relay_setKeyAuthorization RPC method
  • Automatically attaches pending authorizations to eth_fillTransaction requests for the corresponding access key
  • Supports both single-signature and multisig authorizations (saving multisig authorizations once they reach quorum)
  • Validates authorization signatures, expiry, and chain compatibility
  • Removes authorizations once the key is active onchain or when they expire
  • Works with both local relays (via plugins) and remote relays (via keyAuthorization: true option)

This replaces the deprecated KeyAuthorizationManager account option with a relay-based approach that works better with coordinated multisig flows.

Changes

Core Implementation

  • src/tempo/internal/relay/keyAuthorization.ts: New plugin implementation with request handling for relay_setKeyAuthorization, multisig_approveKeyAuthorization, and eth_fillTransaction methods
  • src/tempo/Relay.ts: Added Relay.keyAuthorization export and type definitions
  • src/tempo/Transport.ts: Updated withRelay to detect and advertise key authorization capability
  • src/tempo/internal/relay/plugin.ts: Added plugin registration for capability detection

Integration

  • src/tempo/actions/accessKey.ts: Updated signAuthorization to automatically submit signed authorizations to relays with key authorization storage
  • src/tempo/Account.ts: Deprecated keyAuthorizationManager account option
  • src/tempo/KeyAuthorizationManager.ts: Marked as deprecated

Documentation

  • site/pages/tempo/relay/plugins/key-authorization.mdx: New comprehensive plugin documentation with recipes for single-signature and multisig flows
  • site/pages/tempo/actions/accessKey.signAuthorization.mdx: Added note about relay integration
  • site/pages/tempo/guides/relay/: Updated relay guides to include the new plugin
  • site/vocs.config.ts: Added plugin to navigation

Tests

  • src/tempo/internal/relay/keyAuthorization.test.ts: Comprehensive test suite covering:
    • Default behavior of attaching authorizations to transactions
    • Plain HTTP relay storage and attachment
    • Relays without the plugin still returning signed authorizations
    • Behavior with onchain-authorized keys
    • Admin key authorization restrictions
    • Explicit authorization preservation
    • Expiry handling
    • Malformed authorization cleanup
    • relay_setKeyAuthorization RPC method validation
  • src/tempo/internal/relay/multisig.test.ts: Added integration tests for multisig key authorizations

Changelog

  • .changeset/relay-key-authorization.md: Documented the new feature and deprecation

Test Plan

The PR includes comprehensive test coverage:

  • 426 lines of unit tests in keyAuthorization.test.ts covering all major scenarios
  • Integration tests in multisig.test.ts for coordinated multisig authorizations
  • Tests verify signature validation, expiry handling, chain compatibility, and onchain state checks
  • All existing tests continue to pass

https://claude.ai/code/session_011m6WzEXC4aBoYHHxaU7X9X

Adds a relay plugin that stores signed key authorizations and attaches
them to the access key's next `eth_fillTransaction` until the key is
active onchain. `Actions.accessKey.signAuthorization` saves locally
signed authorizations through `relay_setKeyAuthorization`, and the
plugin stores multisig key authorizations once they reach quorum.

Remote `withRelay` transports opt in with `keyAuthorization: true`.
Deprecates `KeyAuthorizationManager` and the `keyAuthorizationManager`
account option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011m6WzEXC4aBoYHHxaU7X9X
@vercel

vercel Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
viem Ready Ready Preview Oct 9, 2026 6:02am UTC

Request Review

@changeset-bot

changeset-bot Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 30d2d56

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
viem Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T06:04:24.749245Z 30d2d56 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011m6WzEXC4aBoYHHxaU7X9X
@pkg-pr-new

pkg-pr-new Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/viem@5214

commit: 30d2d56

@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: 9b081c9bbd

ℹ️ 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 src/tempo/Transport.ts
Comment on lines +281 to +284
export type Parameters = RelayProxyParameters & {
/** Whether the relay stores key authorizations with `Relay.keyAuthorization`. Defaults to `false`. */
keyAuthorization?: boolean | undefined
}

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 Preserve remote keyAuthorization in the transport type

When the remote overload is called with { keyAuthorization: true }, the implementation adds keyAuthorization to the runtime transport value, but the overload still returns the fixed withRelay.ReturnValue/Relay type containing only { multisig: true }. Consequently, client.transport.keyAuthorization is a type error specifically for remote relays even though the capability is enabled and is correctly inferred in local mode. Make the remote overload generic over these parameters and include { keyAuthorization: true } when the literal option is enabled.

AGENTS.md reference: AGENTS.md:L70-L76

Useful? React with 👍 / 👎.

Comment on lines +53 to +55
await next()
// The multisig plugin validates the config and owner approvals before
// reporting success, so the completed authorization can be stored as is.

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 Revalidate completed multisig authorizations before storing

When a multisig authorization reaches quorum after its expiry, this branch stores it without the expiry check used by relay_setKeyAuthorization; the subsequent fill immediately removes it, so the approval call reports success but the advertised first-use transaction fails with an unauthorized key. Likewise, retrying an already-successful operation after the multisig config changes can restore an authorization whose config is no longer current, causing it to be attached and rejected repeatedly. Re-run the completed authorization through the expiry and multisig verification checks before writing it to the pending store.

Useful? React with 👍 / 👎.

`relay_setKeyAuthorization` now only replaces a pending authorization
that expires earlier, so a replayed older authorization cannot replace
a newer one. Writes use atomic compare-and-set, entries expire with the
authorization (at most 30 days), and authorizations for keys that are
already active are not stored. `Relay.keyAuthorization` now requires an
atomic store.

Also removes the friction log entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011m6WzEXC4aBoYHHxaU7X9X

@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: 30d2d56046

ℹ️ 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 on lines +255 to +257
if (await store.compareAndSet(key, current, value, { expiresAt })) return true
// Another writer replaced the value, so compare against the new one.
return write(store, options)

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 Bound compare-and-set retries

When an atomic store repeatedly loses compareAndSet because of sustained contention, or an adapter returns false without making progress, this recursively retries forever, leaving relay_setKeyAuthorization requests pending and continuously hitting the backing store. The existing multisig operation updater caps the same retry loop at 32 attempts and raises a conflict error; apply a similar bound here so a contended key cannot consume relay resources indefinitely.

Useful? React with 👍 / 👎.

Comment on lines +1385 to +1386
/** Signs a key authorization locally without saving it to a relay. @internal */
export async function inner<

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the signing helper off the public action

Declaring inner as an exported member of the exported signAuthorization namespace exposes Actions.accessKey.signAuthorization.inner at runtime and in the package declarations, unintentionally turning an implementation detail into a public API that consumers can depend on. Move this helper to a module-local function rather than attaching it to the public action.

AGENTS.md reference: AGENTS.md:L99-L99

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
Preview — 30d2d560 Deployed Oct 9, 2026 by vercel[bot]
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.

2 participants