Abstract
This proposal extends ERC-8128 with recursive, attenuating EIP-712 Delegation Grants. A root Ethereum Account can authorize an Ethereum Account or another signing key. Either kind of delegate can further delegate within its granted authority, while the Leaf Delegate signs HTTP requests subject to the grants' constraints without sharing the root key. The complete chain accompanies each request, so a verifier need not pre-register or store Delegation Grants.
Motivation
Ethereum Accounts need short-lived, audience-bound, revocable delegation for session keys, automated agents, and subagents without exposing root keys. Recursive grants allow a delegate to further attenuate authority while requiring every child to narrow, never broaden, what it receives.
EIP-712 lets wallets display grant fields for human approval; HTTP Message Signatures authenticate individual requests. Independent grant and request-signature lifetimes allow persistent sessions with short-lived, non-replayable requests.
Delegates can use Ethereum Accounts or keys for registered HTTP Signature Algorithms, such as ECDSA with P-256 or Ed25519. Delegates that need only HTTP authentication require no onchain account. Those already using an Ethereum wallet can use the same Account for HTTP authentication and separately authorized onchain operations; these grants do not themselves authorize spending. Child grants use the issuer key and profile authorized by the parent grant. Key choice changes neither the Ethereum Principal nor the grant-revocation model.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
This proposal requires the complete ERC-8128 Request Signature profile, including request binding, content integrity, replay storage, candidate parsing, limits, Universal Account verification, and failure selection.
1. Definitions
- Root Account: the Account in the first grant's
issuer; it is the authenticated Principal. - Delegate: the signing identity receiving authority in one grant, identified and verified under its Delegate Profile.
- Delegate Profile: the grant-bound rules for identifying the delegate key and verifying its Request Signature and any child-grant proofs, as specified in Sections 3.5 and 3.6.
- Revocation Account: the Ethereum Account controlling a grant's registry status, derived under Section 5.
- Delegation Grant: the EIP-712
Delegationvalue and its embedded signature. - Delegation Link: the deterministic CBOR encoding of one Delegation Grant in the delegation field.
- Delegation Chain: the ordered links
g0throughgN. - Leaf Delegate: the Delegate receiving authority in the final grant; it is the Request Signature Signer, identified by its Delegate ID.
- Effective Audience, Effective Scopes, Effective Components, Effective Grant Window, Effective Maximum Request Validity, and Effective Non-Replayable Requirement: the authority remaining after applying attenuation through the chain. The Effective Grant Window is the final link's
validAfterthroughvalidUntil.
All other terms have the meanings defined by ERC-8128.
2. HTTP Fields and Signature Class
The ERC-8128-Delegation field is an RFC 9651 Dictionary with consecutive members g0, g1, ... gN in that order. Each value is a Byte Sequence containing one CBOR Delegation Link under Section 3.2.
A route MUST explicitly permit Delegated Request Signatures; otherwise the candidate yields unsupported_delegation. Scope requirements apply in addition to the Principal's application authorization, and an empty route scope set MUST NOT itself enable delegated access.
The Delegated Request Signature has the exact RFC 9651 String parameter tag="erc8128-delegated". Its keyid identifies the Leaf Delegate. It MUST cover the exact delegation-field component with the sf parameter:
"erc-8128-delegation";sfThat component authenticates the parsed and strictly serialized whole Dictionary. Missing that coverage yields delegation_not_covered.
Grant signatures are embedded in links; no separate RFC 9421 authorization signature is used.
A verifier implementing ERC-8128 but not this extension recognizes the class only if configured to do so; recognition without support yields unsupported_delegation. The candidate MUST NOT be reinterpreted as erc8128 or another class.
If application policy would accept a Delegated Request Signature rooted in Principal P, it MUST also accept a direct ERC-8128 Request Signature by P for the same request; grant-specific constraints apply only to the delegated signature.
3. Delegation Grants
3.1 EIP-712 Type
The exact EIP-712 primary type and its member type are:
struct AudienceScope {
string audience;
string[] scopes;
}
struct Delegation {
string issuer;
string delegate;
AudienceScope[] audienceScopes;
bytes32 id;
uint64 epoch;
uint64 validAfter;
uint64 validUntil;
uint32 maxRequestValiditySeconds;
uint32 remainingDelegations;
string delegateProfile;
bool requireNonReplayable;
string[] requiredComponents;
bytes32 parentGrantHash;
}The type string is:
Delegation(string issuer,string delegate,AudienceScope[] audienceScopes,bytes32 id,uint64 epoch,uint64 validAfter,uint64 validUntil,uint32 maxRequestValiditySeconds,uint32 remainingDelegations,string delegateProfile,bool requireNonReplayable,string[] requiredComponents,bytes32 parentGrantHash)AudienceScope(string audience,string[] scopes)The EIP-712 domain has exactly these fields:
name: "ERC-8128 Delegation"
version: "1"
chainId: the chain ID in the grant's Revocation Account
verifyingContract: 0x78471f52e31Dc1d21891D80f257094fF885a3C44g0.issuer MUST be a canonical lowercase-address CAIP-10 Account ID under ERC-8128's grammar; a non-Ethereum root yields bad_delegation_field. Derived fields MUST be reconstructed under Section 3.2 before EIP-712 signing or hashing. delegate and delegateProfile MUST satisfy Section 3.5. delegateProfile MUST conform to the RFC 9651 Token syntax. All unsigned integer fields MUST fit their EIP-712 types. The domain chainId MUST equal the numeric chain ID of the grant's Revocation Account, including for a JWK issuer. audienceScopes MUST be non-empty, and its entries MUST have distinct audience values. validUntil MUST be greater than validAfter, and maxRequestValiditySeconds MUST be nonzero. Each requiredComponents entry MUST encode an RFC 9421 request component as its ASCII component name followed by any strictly serialized RFC 9651 parameters, without the outer quotes used in a signature base; examples are @method, content-type, and @query-param;name="id". @signature-params and the delegation-field component from Section 2 are forbidden. Within each array, values SHOULD be unique.
Violations of these structural requirements yield bad_delegation_field before cryptography. Link encoding requirements are specified in Section 3.2.
Signers SHOULD order audienceScopes entries by ascending bytewise audience, and each entry's tokens in ascending bytewise order. Audience, scope, and component comparisons ignore array order. EIP-712 hashing does not: signers and verifiers MUST preserve encoded order without sorting or other canonicalization.
Grant signing and proof verification follow Section 3.6. The EIP-712 digest remains the grant identifier used by parentGrantHash for every issuer profile.
3.2 Link Encoding and Limits
Each Byte Sequence is one Delegation Link encoded as a definite-length CBOR array. Fields appear in the following order, omitting rows whose condition is not met:
| Element | Present when | CBOR type and constraint |
|---|---|---|
issuer | Root only | text string, canonical Ethereum Account ID |
delegate | Always | text string, Account ID or public JWK under Section 3.5 |
audienceScopes | Always | array of entries, each a two-element array of the audience text string and an array of scope text strings |
id | Always | byte string, exactly 32 bytes |
epoch | Ethereum issuer | unsigned integer |
validAfter | Always | unsigned integer |
validUntil | Always | unsigned integer |
maxRequestValiditySeconds | Always | unsigned integer |
remainingDelegations | Always | unsigned integer |
delegateProfile | Always | text string, RFC 9651 Token |
requireNonReplayable | Always | Boolean |
requiredComponents | Always | array of text strings |
parentGrantHash | Child only | byte string, exactly 32 bytes |
signature | Always | non-empty byte string |
The root has 13 elements. A child has 13 elements when its parent's delegateProfile is an Ethereum profile, and 12 otherwise. No placeholder occupies an omitted field's position.
Signers and verifiers MUST reconstruct the full Section 3.1 value in root-to-leaf order:
- Root
parentGrantHashis 32 zero bytes. - Child
issueris its parent's Delegate ID under Section 3.5. - A JWK-issued child's
epochis its parent'sepoch.
These derived fields remain part of the signed EIP-712 value. Computed Delegate IDs and grant digests can be reused throughout verification.
Encoding MUST follow the core deterministic requirements of RFC 8949 Section 4.2.1. Only unsigned integers, byte strings, UTF-8 text strings, arrays, and one-byte Boolean values are permitted. Strings and arrays MUST have definite lengths; CBOR heads MUST use their shortest form. The top-level array MUST match the element count, order, and constraints above, with no trailing bytes. Invalid UTF-8 or any other encoding or data-model violation MUST yield bad_delegation_field before cryptography.
Nested CBOR arrays are permitted only for requiredComponents, audienceScopes, each audienceScopes entry, and the scope array within that entry. Deeper nesting MUST yield bad_delegation_field; decoders SHOULD enforce this depth limit.
The field MUST contain at least g0, use consecutive lowercase decimal indices without leading zeroes, contain no other member, and preserve ascending member order. A violation yields bad_delegation_field.
ERC-8128's candidate and signature-field limits apply across Direct and Delegated candidates. This extension adds these defaults:
| Input | Default limit |
|---|---|
| Combined ERC-8128-Delegation field | 16 KiB |
| Delegation Link Byte Sequence | 8 KiB |
Entries in audienceScopes or requiredComponents | 32 |
Scope tokens across all audienceScopes entries of one grant | 128 |
UTF-8 bytes in delegate for a registered algorithm | 2 KiB |
| UTF-8 bytes in any other text string | 256 |
The combined-field and Delegation Link byte limits MUST be enforced before CBOR decoding. The entry, token, and text-string limits MUST be enforced during CBOR decoding; the decoder MAY initially bound delegate at 2 KiB, then apply the 256-byte limit if its profile is Ethereum. Exceeding any limit yields delegation_too_large before cryptography. Deployments MAY use smaller documented limits.
Deployments with lower HTTP field limits SHOULD reduce chain depth or per-grant limits accordingly.
3.3 Grant Semantics
A grant's Audiences are the audience values of its audienceScopes entries. Each is a canonical RFC 6454 origin: it MUST be the ASCII serialization of a non-opaque origin, without a trailing slash, trailing-dot host, wildcard, path, query, or fragment. It MUST use https. Verifiers MUST NOT repair a non-canonical value; one yields bad_delegation_field before cryptography.
id identifies a grant within its revocation namespace under Section 5. Issuers SHOULD use a unique value for every grant. Reusing one intentionally groups grants in that namespace for collective revocation.
epoch is the registry epoch of this grant's Revocation Account. Ethereum issuers read it from the registry; JWK-issued grants inherit it under Section 3.2. validAfter and validUntil define the grant's absolute validity window. A deployment MAY configure a maximum window for any grant. A route MAY additionally configure a maximum Effective Grant Window, measured as gN.validUntil - gN.validAfter. Exceeding either maximum yields grant_validity_too_long before cryptography. With ERC-8128 skew s, now < validAfter - s yields grant_not_yet_valid and now > validUntil + s yields grant_expired.
maxRequestValiditySeconds bounds request.expires - request.created independently of grant lifetime. Exceeding the effective value yields delegation_request_validity_exceeded. A request whose own window is not contained within the leaf grant window, allowing skew only at the grant validAfter boundary, yields request_outside_grant_window.
remainingDelegations bounds the number of additional delegation levels below this grant: zero forbids child grants. It does not limit the number of sibling grants or HTTP requests.
delegateProfile and delegate bind the delegate's Request Signatures and child-grant proofs to the issuer-approved profile and key under Sections 3.5 and 3.6. A child grant's delegate and delegateProfile describe its recipient and MUST NOT select the key or profile used to verify its issuer's proof.
requireNonReplayable=true requires ERC-8128 Non-Replayable posture, including atomic nonce consumption; false adds no grant-level replay restriction. Verifier and route policy may still forbid Replayable requests. A Replayable request when any link sets requireNonReplayable=true yields delegation_nonce_required.
requiredComponents lists minimum signature coverage beyond the mandatory delegation-field component. Signers may cover more; routes may require more. ERC-8128 request binding and replay posture remain independent. If the verifier cannot reconstruct a required derived component or process one of its parameters, it yields delegation_components_unsupported. If the verifier supports the component but the request omits it, it yields delegation_components_uncovered.
Each entry's scopes contains case-sensitive capabilities defined by the service at its audience; they apply only at that Audience, with AND route semantics. Each value MUST conform to the RFC 9651 Token syntax and to the limits in Section 3.2; a violation yields bad_delegation_field before cryptography. An entry MAY contain no tokens: the Delegate can sign requests to that Audience but holds no scopes there. A link's scopes at origin O are the tokens of its entry whose audience is O. Unknown tokens grant no authority. A verifier need not implement application scopes to accept a chain whose every entry contains no tokens on a route that requires none. A verifier unable to process scopes yields unsupported_scopes only when at least one link grants a token at the effective request origin. The route's required scopes are always compared with the chain's Effective Scopes at the effective request origin. A valid authenticated chain missing any route-required scope yields insufficient_scope with HTTP 403.
3.4 Recursive Continuity and Attenuation
Each child's parentGrantHash MUST equal its immediate parent's EIP-712 digest; otherwise the candidate yields delegation_chain_discontinuous. Reconstruction under Section 3.2 supplies the child issuer. The Delegated Request Signature keyid MUST equal the leaf's Delegate ID; inequality yields delegate_mismatch.
The Principal is the Root Account identified by g0.issuer. The Signer is the Leaf Delegate, identified by its Delegate ID.
Each child MUST satisfy:
- each entry's
audienceis in the parent's Effective Audience; - each entry's tokens are a subset of the parent's Effective Scopes at that
audience, including the empty set; validAfter >= parent.validAfterandvalidUntil <= parent.validUntil;maxRequestValiditySeconds <= parent.maxRequestValiditySeconds; andremainingDelegations < parent.remainingDelegations.
Any violation yields delegation_attenuation_violation.
For a valid chain or prefix, Effective Audience is the leaf's Audiences, Effective Scopes at origin O are the leaf's scopes at O, Effective Grant Window is its validAfter through validUntil, and Effective Maximum Request Validity is its maxRequestValiditySeconds. Effective Components are the union of all requiredComponents sets; Effective Non-Replayable Requirement is the logical OR of all requireNonReplayable values. These rules also apply to a single-link chain. Audiences and tokens a child omits are not inherited; no child can relax an ancestor's Non-Replayable requirement.
The default maximum chain depth is four links. A deployment MAY configure another positive maximum and MUST document it. Exceeding it yields delegation_chain_too_long before cryptography.
3.5 Delegate Profiles
Verifiers MUST support the Ethereum profiles below and MAY support asymmetric algorithms from the IANA HTTP Signature Algorithms registry with standardized public JWK representations. Symmetric algorithms are forbidden and yield bad_delegation_field. An unrecognized profile yields unsupported_delegate_profile. Every profile used in the chain MUST be supported for both request and grant proofs. The Principal MUST remain an Ethereum Account. A Delegate using a supported profile can issue child grants within remainingDelegations to Delegates using any supported profile.
delegateProfile | delegate | Request Signature verification |
|---|---|---|
erc8128 | Canonical ERC-8128 CAIP-10 Account ID | ERC-8128 Universal Account verification |
erc8128-eoa | Canonical ERC-8128 CAIP-10 Account ID | ERC-8128 strict local ECDSA, even if the address later obtains code; wrappers and contract callbacks are forbidden |
| An asymmetric HTTP Signature Algorithm identifier | Public JWK as specified below | The registered algorithm's RFC 9421 signing and verification rules over signature base M |
For a registered algorithm, delegate MUST contain the public JWK representation used as the thumbprint hash input in RFC 7638 Section 3: only the required public-key members, serialized as defined there. Duplicate members, private or symmetric key material, and additional members are forbidden. The key MUST be valid for the selected algorithm under the referenced algorithm and key specifications. Invalid key material, serialization, or profile/key relationships yield bad_delegation_field. A verifier without support for the selected algorithm or key type yields unsupported_delegate_profile; it need not interpret an unsupported key type.
A grant's Delegate ID is delegate for an Ethereum profile, or the RFC 9278 SHA-256 JWK Thumbprint URI computed from the UTF-8 bytes of delegate for a registered algorithm.
The request retains tag="erc8128-delegated" and MUST use the leaf's Delegate ID as keyid. For a registered algorithm, alg MAY be omitted: the grant's delegateProfile supplies the algorithm metadata under RFC 9421 Section 3.2. If present, alg MUST be an RFC 9651 String equal to delegateProfile; otherwise the candidate yields unsupported_algorithm. The Ethereum profiles retain ERC-8128's requirement to omit alg. A verifier MUST use the grant-bound key and profile and MUST NOT substitute request-supplied key material, dereference keyid, or fall back to another profile after failure. This proposal defines no new HTTP Signature Algorithm and does not redefine registered algorithms, key formats, hashing, or signature encodings.
For registered algorithms, these rules replace only ERC-8128's Account-specific keyid, served-chain, alg, and signing/verification rules. A missing, non-String, or malformed request keyid yields invalid_keyid; a valid identifier differing from the leaf yields delegate_mismatch. Such keys have no Account-code classification or chain, and ERC-8128's mode parameter does not apply to them. All other request, grant, and revocation checks remain unchanged. The replay key is (Delegate ID, nonce) across grants and profiles using that identifier.
For a Replayable request using a registered algorithm, early invalidation MUST use per-Signer not-before state or a fingerprint of (Signer, signature base), not signature bytes, because the same signature base can have multiple valid signatures. A signer-triggered invalidation request MUST itself be a Request-Bound, Non-Replayable Delegated Request Signature by that key, satisfying all grant and route constraints. This replaces ERC-8128's direct Request Signature requirement only for non-Ethereum signers; accepting Replayable requests without such an invalidation mechanism remains forbidden.
3.6 Grant Proofs
For every grant, let B be the 66-byte EIP-712 signing input 0x19 || 0x01 || domainSeparator || hashStruct(grant), and let D = keccak256(B). Domain construction follows Section 3.1. The embedded proof signs B as follows:
| Issuer | Signing and verification |
|---|---|
| Ethereum root | ERC-8128 Universal Account verification of D |
Child whose parent names erc8128 | ERC-8128 Universal Account verification of D using the parent's delegate |
Child whose parent names erc8128-eoa | ERC-8128 strict local ECDSA over D using the parent's delegate; no Account callback or wrapper |
| Child whose parent names a registered asymmetric algorithm | The parent's algorithm and public JWK in delegate, applying that algorithm's signing, verification, and signature encoding to B as the message input |
Ethereum grant proofs use the EIP-712 digest directly, including for ERC-1271 and ERC-6492; no personal_sign prefix is added. Registered algorithms use B, not D, with their prescribed hashing. These are embedded grant proofs, not HTTP Message Signatures; RFC 9421 supplies the algorithm procedures, while EIP-712 supplies the grant message and domain separation. No HTTP signature parameters or independently supplied issuer key accompany a grant proof.
For each child, the verifier MUST obtain the issuer's profile and key exclusively from its immediate parent, enforce the continuity and attenuation rules, and verify every ancestor through the Ethereum root. A valid proof without that complete authorized chain conveys no authority. A definitive invalid proof yields bad_grant_signature; unavailable classification or verification yields grant_verification_unavailable. Profile fallback is forbidden.
3.7 Grant-Proof Helper
The reference helper at verifyingContract supports erc8128, erc8128-eoa, and ecdsa-p256-sha256 grant proofs. Verifiers MAY use it or an equivalent offchain implementation of Section 3.6. Other registered algorithms require another implementation.
Both verification paths MUST use the domain in Section 3.1. Offchain verification does not require a helper deployment or call. Callers using the helper MUST check its pinned runtime code hash and use the grant's Revocation Account chain.
The helper checks individual grant proofs and issuer-key binding only. Request verifiers remain responsible for all other checks in Sections 3 through 5; a successful helper result does not establish authorization.
On a chain with neither the RIP-7212 P-256 precompile nor the helper's fallback P-256 verifier, the helper's P-256 entry point returns false for every proof. That result is not a definitive invalid proof: a caller that cannot establish P-256 verification on that chain yields grant_verification_unavailable, not bad_grant_signature.
4. Verification
4.1 Candidate Evaluation
Delegated candidates participate in ERC-8128 Candidate Evaluation and Limits. A verifier supporting both classes counts both exact tags against one default budget of eight and evaluates them in Signature-Input Dictionary wire order. An RFC 9651 syntax error remains a whole-field signature_input_invalid; only a well-formed candidate that fails this extension is skippable.
Within a Delegated candidate, grant-window failures (grant_not_yet_valid, grant_expired) MUST take precedence over ERC-8128 request-window failures. Implementations MAY reorder checks if failure selection is unchanged.
Before any account-classification RPC, the verifier MUST validate field and chain limits, wire structure, profile-bound identifiers and keys, domain relationships, delegate-profile support and algorithm, explicit delegated-route acceptance, continuity, attenuation, grant and request times, Audience, request binding, delegation-field coverage, component support and coverage, content digest, scope-processing capability, Principal policy, and replay posture. Each deployment MUST document its account-classification RPC budget; the default maximum per candidate is 2 + chain depth. Failure to obtain required classification within the budget yields the corresponding verification-unavailable code and MUST NOT prevent evaluation of later candidates.
The verifier then processes a candidate in this order:
- Reconstruct the effective request origin from the trusted effective target URI. Absence from the Effective Audience yields
audience_mismatch. - Verify the leaf Request Signature using its Delegate Profile under Section 3.5. Invalidity yields
bad_signature; unavailability yieldssignature_verification_unavailable. - Compute any EIP-712 digests not already obtained during reconstruction, then verify link signatures from
gNthroughg0under Section 3.6 or a Section 4.2 cache. Invalidity yieldsbad_grant_signature; unavailability yieldsgrant_verification_unavailable. - Check revocation for every link as specified in Section 5. A revoked link yields
authorization_revoked; an epoch mismatch yieldsauthorization_epoch_mismatch; unavailable state yieldsrevocation_unavailable. - Enforce all route-required scopes unconditionally against Effective Scopes at the request origin, yielding
insufficient_scopewhen any is absent. - For a nonce, atomically consume the ERC-8128 replay key
(Leaf Signer, nonce)last. Without a nonce, enforce ERC-8128 early-invalidation policy. - Select the candidate as winner.
A verifier MUST NOT emit insufficient_scope before all candidate proofs verify. ERC-8128's observable-equivalence and nonce rules apply.
Successful authentication returns:
{
principal: RootAccountId,
signer: LeafDelegateId, // canonical Ethereum Account ID or JWK Thumbprint URI
delegated: true,
binding: "request-bound" | "class-bound",
replay: "non-replayable" | "replayable",
delegationIds: Bytes32[]
}4.2 Grant-Proof Cache
A verifier MAY cache only a positive grant-proof result. The cache key MUST unambiguously encode the pair (grant digest, signature bytes). The digest binds every grant and domain field, including the parent digest that fixes a child issuer's profile and key. Cache use MUST follow continuity validation. For Universal Account results, cache invalidation MUST account for Account-code changes and context-dependent ERC-1271 validity according to deployment policy. This cache never replaces Section 5 revocation checks.
5. Canonical Revocation Registry
The Ethereum mainnet (eip155:1) deployment is canonical and defines the registry address and runtime code hash. Each grant uses the registry at that address on the EIP-155 chain identified by its Revocation Account. A verifier MUST derive that Account as follows, verify the registry runtime code hash, and MUST NOT substitute another chain or registry address.
| Grant issuer | Revocation Account | Registry handle |
|---|---|---|
| Ethereum Account | issuer | id |
| JWK delegate | Parent's Revocation Account | `keccak256(parentGrantHash |
A JWK-issued grant inherits the nearest Ethereum issuer's registry namespace and chain. Its handle binds the parent grant and child id, so reusing an id under another parent does not group those grants. The Revocation Account can revoke a subgrant and its descendants by this handle. Registry mutation authorization is verified against the Ethereum Revocation Account under the interface below. Every ancestor's status is checked, so revoking an ancestor or advancing its Revocation Account's epoch invalidates the subtree.
The reference deployment MUST use deterministic CREATE2, and this proposal will fix its address and runtime code hash before it advances beyond Draft. A registry on another chain MUST use that address and runtime code hash. Until those values are populated, production deployments MUST NOT claim conformance to this extension. Absence, code-hash mismatch, or unavailability of the required deployment yields revocation_unavailable.
| Property | Canonical value |
|---|---|
| Reference chain | Ethereum mainnet (eip155:1) |
| Address | To be assigned by the deterministic reference deployment |
| Runtime code hash | To be assigned by the deterministic reference deployment |
The complete external interface is:
interface IERC8395RevocationRegistry {
function status(address issuer, bytes32 id)
external view returns (bool isRevoked, uint64 currentEpoch);
function revoke(bytes32 id) external;
function revokeBySig(address issuer, bytes32 id, bytes calldata sig) external;
function advanceEpoch() external returns (uint64 newEpoch);
event Revoked(address indexed issuer, bytes32 indexed id);
event EpochAdvanced(address indexed issuer, uint64 newEpoch);
}revoke(id) permanently revokes (msg.sender, id). advanceEpoch() increments and returns msg.sender's independent uint64 epoch, thereby invalidating every outstanding grant using that Revocation Account whose epoch differs. Epochs start at zero, advance monotonically, and MUST NOT wrap. The registry MUST provide neither individual unrevocation nor epoch reduction.
For a JWK-issued grant, callers pass its derived registry handle as id to these operations.
Both revoke and a successful revokeBySig MUST emit Revoked. advanceEpoch MUST emit EpochAdvanced with the returned value.
revokeBySig is relayable. sig signs this EIP-712 value:
struct Revocation {
address issuer;
bytes32 id;
}Its type string is Revocation(address issuer,bytes32 id). Its domain name is "ERC-8128 Delegation Registry", its version is 1, its chain ID is the chain on which the registry is deployed, and its verifyingContract is the canonical registry address. The registry MUST require the signed issuer to equal the function argument. It uses strict low-s ECDSA when issuer has no code on that chain and ERC-1271 when it has code, supplying the EIP-712 digest directly. Replaying the message is harmless because revocation is permanent.
For every link, the verifier calls status(address(RevocationAccount), registryHandle) using the Account, handle, and chain derived above. A grant is active only when isRevoked == false and currentEpoch == g[i].epoch.
A verifier whose selected chain state can trail the chain head, such as an indexed or finality-delayed view, MAY also treat a link as active when isRevoked == false, g[i].epoch == currentEpoch + 1, and the verifier's current time is at most 60 seconds after g[i].validAfter. This admits a grant signed immediately after an epoch advance that the verifier's chain state has not yet observed. Only the Revocation Account can sign such a grant, and it can only be one epoch ahead. The verifier MAY cache that result under the positive status cache rules below, but never beyond the end of that window. Once the verifier's chain state shows currentEpoch == g[i].epoch, the ordinary rule applies; any other observed epoch, or the window's end, makes the link inactive.
Before signing a grant, an Ethereum issuer MUST read currentEpoch from its chain's registry and copy it into epoch. A JWK issuer inherits its parent's epoch under Section 3.2; creating a subgrant requires no registry mutation.
A positive status cache entry MUST bind the registry chain and address, Revocation Account address, registry handle, and signed epoch. Its TTL MUST NOT exceed the remaining grant lifetime. It also MUST NOT exceed 60 seconds unless the deployment documents a larger client-visible bound. That TTL bounds local reuse of a previously observed status, not the time from transaction submission to observation in the verifier's selected chain state. An expired entry cannot be extended stale-while-error. Registry reads follow the verifier's chain-state policy.
Registry or provider failure without an unexpired positive entry yields revocation_unavailable; accepting on error is forbidden.
6. Failure Responses
ERC-8128 failure codes and status rules apply, with the extension codes listed in Section 6.2.
delegation_too_large and delegation_chain_too_long use HTTP 400. insufficient_scope uses 403. grant_verification_unavailable and revocation_unavailable use 503. All other extension failures use 401. Each 401 MUST include ERC-8128's ERC8128 challenge with its error parameter. When a response body is sent, the ERC-8128 reason requirement applies.
6.1 Authority Challenge Parameters
An ERC8128 challenge MAY include these auth-params to describe the authority that would satisfy the target route:
scope: 1 to 32 distinct route-required RFC 9651 Tokens, each at most 256 bytes, separated by one ASCII space, with AND semantics.audience: the canonical HTTPS origin that a Delegation Chain's Effective Audience must contain.max_validity: the ASCII decimal representation, without leading zeroes, of an integer from1through9007199254740991that bounds the Effective Grant Window in seconds.max_depth: the same integer syntax, bounding the accepted Delegation Link count.
Senders MUST use quoted-string syntax for these parameters. If a recognized authority parameter is repeated or invalid, all ERC-8395 authority metadata in that challenge MUST be ignored; ERC-8128 parameters remain independently usable. Unknown auth-params are ignored. Omission does not advertise unconstrained support, and a runtime challenge is authoritative over discovery metadata for the next request.
For a Delegated Request Signature, the Effective Audience, the Effective Scopes at the request origin, the Effective Grant Window, and the link count MUST satisfy the advertised values.
At runtime, an Accept-Signature member covering the delegation field advertises Delegated Request Signature support; max_depth alone does not.
A verifier returning insufficient_scope, audience_mismatch, grant_validity_too_long, or delegation_chain_too_long SHOULD include the corresponding parameter unless disclosure is a risk. A 403 insufficient_scope response SHOULD include an ERC8128 challenge. Required request components and supported registered algorithms are advertised using RFC 9421 Accept-Signature. A verifier returning unsupported_delegate_profile SHOULD include supported alternatives there. For example, a delegated member with alg="ecdsa-p256-sha256" advertises that algorithm; omission of alg advertises Ethereum verification. Algorithm hints do not authorize changing the profile or key in an existing grant.
HTTP/1.1 403 Forbidden
WWW-Authenticate: ERC8128 error="insufficient_scope", scope="orders:create", audience="https://merchant.example", max_validity="3600", max_depth="2"
Accept-Signature: direct=("@scheme" "@authority" "@method" "@path" "@query" "content-digest" "content-type");keyid;created;expires;tag="erc8128";nonce, delegated=("@scheme" "@authority" "@method" "@path" "@query" "content-digest" "content-type" "erc-8128-delegation";sf);keyid;created;expires;tag="erc8128-delegated";nonce6.1.1 ERC-8128 Discovery Extensions
ERC-8395 defines these OPTIONAL ERC-8128 discovery members:
| Location and member | Type, bounds, and meaning |
|---|---|
Route policy scopes | A non-empty JSON array of at most 32 distinct strings, each at most 256 bytes and conforming to the RFC 9651 Token syntax, required under AND semantics. |
Route policy audience | A canonical HTTPS origin under Section 3.3. |
Route policy max_depth | An integer from 1 through 9007199254740991 bounding the Delegation Link count; presence advertises Delegated Request Signature support in discovery. |
Route policy max_grant_validity_sec | An integer from 1 through 9007199254740991 bounding the Effective Grant Window in seconds. |
An invalid recognized member MUST be ignored and MUST NOT imply the capability or constraint it would otherwise advertise. All other ERC-8128 discovery rules and limits apply.
ERC-8128's top-level name and scopes discovery members describe the origin publishing them. A wallet displaying a grant SHOULD present each audienceScopes entry under its Audience, with that Audience's own name and token descriptions, showing an undescribed token as the token itself. It MUST NOT present descriptions supplied by the requesting application or by another origin as an Audience's.
6.2 Failure Code Index
This non-normative index maps extension codes to conditions:
| Code | Condition |
|---|---|
unsupported_delegation | The extension class is recognized but unsupported, or the route does not explicitly accept delegated authentication |
unsupported_delegate_profile | The verifier does not support the selected delegate algorithm or key type |
bad_delegation_field | The field, CBOR link, grant, or domain is structurally invalid |
delegation_too_large | An extension byte, array, or text-string limit is exceeded |
delegation_not_covered | The request omits whole-field ;sf coverage |
delegate_mismatch | Request keyid differs from the Leaf Delegate's Delegate ID |
delegation_chain_too_long | Configured link depth is exceeded |
delegation_chain_discontinuous | A child's parentGrantHash differs from its immediate parent's EIP-712 digest |
delegation_attenuation_violation | A child broadens parent authority |
grant_not_yet_valid | A grant window has not opened |
grant_expired | A grant window has closed |
grant_validity_too_long | A configured per-grant or route Effective Grant Window maximum is exceeded |
request_outside_grant_window | The request window is outside the leaf grant |
delegation_request_validity_exceeded | Request validity exceeds the effective grant maximum |
delegation_components_unsupported | The verifier cannot process a required signature component or its parameters |
delegation_components_uncovered | The Request Signature omits a supported required component |
delegation_nonce_required | Effective Non-Replayable Requirement forbids Replayable posture |
audience_mismatch | Effective Audience excludes the request origin |
unsupported_scopes | Scope tokens granted at the request origin cannot be processed |
insufficient_scope | Effective Scopes at the request origin omit a route requirement |
bad_grant_signature | A grant proof is definitively invalid |
grant_verification_unavailable | A grant proof cannot be classified or verified |
authorization_revoked | Any link's registry handle is revoked |
authorization_epoch_mismatch | Any link epoch differs from its Revocation Account's current epoch |
revocation_unavailable | Registry state cannot be obtained |
Rationale
Embedded EIP-712 proofs
Each grant exposes its fields for review at signing time and has its own verifiable proof. Child-proof verification requires the issuer key and profile from the parent grant; authorization requires the complete chain. Embedding the proof eliminates RFC 9421 label correlation for reusable authority and binds each child to its parent digest. Carrying the complete chain removes verifier-side grant storage; replay and revocation checks retain their state requirements.
Derived grant fields
One EIP-712 type preserves the full signing context while CBOR omits derivable fields. Child parent digests remain on the wire to permit parallel grant hashing. Deriving JWK identifiers reuses required thumbprint calculations; reconstruction adds no key lookup, registry read, or cryptographic operation. Independently selectable authority fields remain explicit.
Clear signing
The grant domain's chain ID and verifyingContract provide a stable context for clear-signing metadata without adding display-only grant members.
Monotonic recursive authority
Subset, nested-window, remaining-delegation, maximum-request-validity, non-replayability, and component-union rules make the effective authority computable locally. No child can replace its parent context or regain authority removed earlier in the chain.
Per-Audience scopes
Each service defines its own tokens, so one token can mean different things at different origins. Binding tokens to their Audience keeps each service's scopes from conferring authority elsewhere and lets wallets describe them with that service's metadata.
Delegate profile choice
Grants bind keys and verification rules; existing standards supply the cryptography. RFC 9421 keyid identifies a key without distributing it. Carrying the public JWK permits self-contained verification; binding its algorithm prevents substitution.
| Delegate choice | Typical use |
|---|---|
| Ethereum Account | A Delegate already using an Ethereum wallet, an onchain identity, or contract-defined verification policy |
ecdsa-p256-sha256 | Browser sessions using WebCrypto or Delegates using compatible platform APIs or hardware-backed keys |
ed25519 or another supported registered asymmetric algorithm | Services or automated agents using existing compatible signing infrastructure |
WebAuthn assertions sign authenticator and client data and require relying-party and origin checks; they are not ordinary ecdsa-p256-sha256 HTTP signatures. WebAuthn can authorize session-key provisioning or back an Ethereum Account's verification policy. Direct use of assertions as HTTP signatures requires a separately specified HTTP-signature algorithm binding; this proposal does not define one or assign a WebAuthn alg value.
Canonical registry deployment
The canonical address and runtime code hash make grant meaning independent of verifier-selected coordinates. Ethereum issuers select their own chain; JWK issuers inherit an Ethereum ancestor's revocation namespace. This preserves selective subtree revocation without requiring the registry to implement every offchain signature algorithm.
Resource-defined authority
The challenge parameters describe a route's delegation requirements, helping clients obtain suitable grants without making delegation mandatory.
Backwards Compatibility
Delegated signatures use a distinct tag and do not change direct ERC-8128 authentication. Additional delegate algorithms are optional.
Test Cases
Test vectors and expected results cover grant proofs, HTTP signatures, delegation chains, and failure cases.
Implementations MUST also run every applicable ERC-8128 negative vector against the Delegated Request Signature. In particular, malformed Signature-Input remains a whole-field failure, while a well-formed invalid candidate is skippable.
Reference Implementation
Verifier pseudocode illustrates the verification flow.
Security Considerations
Human approval and authority metadata
Wallets SHOULD display the resource origin, Issuer, Delegate and its profile, each Audience with its scopes, validity window, maximum request validity, remaining delegations, required components, Non-Replayable requirement, and parent relationship. When available, wallets SHOULD obtain the requesting origin from trusted interaction context and display it distinctly from the Audiences and Delegate. Audience membership alone does not establish the requesting application's trustworthiness. Clients MUST apply their own authorization policy and MUST NOT treat an authority challenge as user approval or blindly sign typed data supplied by an untrusted party. Resources SHOULD omit optional authority parameters when disclosure would create a security or privacy risk.
Permanent EOA profile
delegateProfile="erc8128-eoa" preserves raw-key semantics for both the delegate's Request Signatures and its child-grant proofs. A Delegate migrating to an SCA cannot use contract verification under that grant and needs a reissued grant. Issuers should keep such grants short.
Revocation latency
Status-cache TTL bounds local reuse as specified in Section 5. Chain-state observation and grant-proof caching also affect when changed authorization is enforced. Short-lived grants remain subject to the same registry checks.
Delegate key custody
A non-extractable key reduces key export risk but does not prevent malicious code in the signing context from using it. The verifier cannot infer hardware protection, non-extractability, or user presence from the selected algorithm or a valid signature. Key loss requires a new grant; cross-device synchronization is outside this proposal. Disposable authentication keys should not be presented as payment addresses. Delegates that intentionally hold funds need durable wallet custody and recovery independent of grant lifetime.
Local subgrant revocation
A service can optionally let an authenticated issuer deny its own subgrant by grant digest, using signed HTTP requests and service-managed state. Such a denial must apply to every descendant presented to that service, and the service must establish the revoker's authority over the targeted grant. This is application policy, not globally discoverable registry revocation. Grant-wide offchain revocation requires delivery, enforcement, and acknowledgment from every Audience; partial delivery leaves other Audiences unaffected. Revoking an Ethereum ancestor remains the global fallback; deleting a JWK private key does not invalidate already-issued grants.
Verification state
Each request requires leaf-signature verification, every grant proof, every link's registry status, and ERC-8128 replay enforcement. Ethereum-ancestor revocation of a JWK-issued subgrant needs its derived handle; revoking the ancestor grant itself also revokes all descendants, including unknown subgrants. Only the leaf nonce is consumed; grants have no request nonces. Proof verification under registered asymmetric algorithms and erc8128-eoa is local, including for child grants; the root and erc8128 issuers use Universal Account verification. Active grants sharing a Revocation Account must match its current epoch, but each registry handle must still be checked. Permitted caches may avoid repeated work but do not remove those checks. Application scopes remain independent; no additional Principal-signature denylist or stored session record is required. Registry revocation alone does not satisfy ERC-8128's signer-triggerable invalidation requirement for Replayable requests.
Registry-chain selection
Ethereum issuers SHOULD prefer Ethereum mainnet. An issuer whose SCA cannot be deployed on mainnet MAY instead use a supported chain on which it can be deployed and verified. Issuers SHOULD keep their grants on one chain whenever possible: one advanceEpoch() call then invalidates all of that issuer's outstanding grants on that registry, while grants spread across chains require advancing the epoch on each registry.
RPC exhaustion and ordering
All local policy, time, coverage, digest, continuity, and attenuation checks precede RPC. The leaf proof precedes grant proofs, which proceed leaf-to-root. Deployments must enforce and document the account-classification budget and separately rate-limit registry reads.
Replay space
The replay key remains (Leaf Signer, nonce), so grants sharing a Signer also share replay space. A nonce collision can reject a legitimate request but cannot authenticate an unauthorized one. Signer-generated 128-bit randomness makes accidental collisions negligible.
CORS and transformations
Browser deployments need the delegation field in CORS allowlists. Intermediary transformation of the field or any covered component invalidates the Request Signature.
Privacy and confidentiality
Every verifier receives the complete Delegation Chain, which can reveal delegation structure and correlate activity. Issuers SHOULD use purpose-specific delegate keys, narrow Audience sets, and unique IDs; wallets SHOULD use a distinct Account per Audience when possible. TLS protects the chain in transit, not from the verifier.
Copyright
Copyright and related rights waived via CC0.
