Network Working Group D. Borthwick
Internet-Draft InsumerAPI
Intended status: Informational 27 September 2026
Expires: 31 March 2027
Wallet State Attestation: Signed Booleans about On-Chain State
draft-borthwick-wallet-state-attestation-01
Abstract
This document defines Wallet State Attestation: a primitive in which
an issuer reads on-chain state for a wallet address, evaluates one or
more operator-defined conditions, and returns a cryptographically
signed boolean (or structured fact profile) that any verifier can
check offline using a published JWKS endpoint. The primitive enables
condition-based access decisions without identity presentation,
credential exchange, or contact with the issuer at verification time
beyond a copy of its published key set and the documentation that
accompanies it. This document describes the request and response
surface, the signing algorithm posture, the JWKS discovery pattern,
and the security and privacy considerations of the primitive. It
does not define a new wire protocol; rather, it profiles existing
IETF building blocks (JWT, JWKS, JOSE) for the wallet-state-
attestation use case.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 31 March 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
Borthwick Expires 31 March 2027 [Page 1]
Internet-Draft Wallet State Attestation September 2026
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . 3
1.2. Position Relative to Identity Primitives . . . . . . . . 3
1.3. Position Relative to Credentials . . . . . . . . . . . . 3
1.4. Position Relative to Other Documents . . . . . . . . . . 4
1.5. Use Cases . . . . . . . . . . . . . . . . . . . . . . . . 5
2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 5
3. Architecture Overview . . . . . . . . . . . . . . . . . . . . 7
4. Request Shape . . . . . . . . . . . . . . . . . . . . . . . . 8
5. Response Shape . . . . . . . . . . . . . . . . . . . . . . . 9
6. Signing Algorithm . . . . . . . . . . . . . . . . . . . . . . 11
6.1. Signing Schemes . . . . . . . . . . . . . . . . . . . . . 12
6.2. Signature Encoding and Additional Algorithms . . . . . . 13
6.3. Companion Signatures . . . . . . . . . . . . . . . . . . 13
7. JWKS Discovery . . . . . . . . . . . . . . . . . . . . . . . 16
8. JWT Format . . . . . . . . . . . . . . . . . . . . . . . . . 17
9. Optional Merkle Proofs . . . . . . . . . . . . . . . . . . . 19
10. Security Considerations . . . . . . . . . . . . . . . . . . . 19
10.1. Single-Issuer Architecture . . . . . . . . . . . . . . . 20
10.2. Freshness Anchors and Replay Considerations . . . . . . 20
10.3. Refuse-on-Observation-Failure . . . . . . . . . . . . . 21
10.4. What This Primitive Does Not Protect Against . . . . . . 21
10.5. Algorithm Agility . . . . . . . . . . . . . . . . . . . 21
11. Privacy Considerations . . . . . . . . . . . . . . . . . . . 22
11.1. Boolean by Default . . . . . . . . . . . . . . . . . . . 22
11.2. Merkle Mode Trade-Off . . . . . . . . . . . . . . . . . 22
11.3. No Identity Attributes . . . . . . . . . . . . . . . . . 23
11.4. Wallet Address Handling . . . . . . . . . . . . . . . . 23
11.5. Awareness of Observation . . . . . . . . . . . . . . . . 23
12. Verification Procedure . . . . . . . . . . . . . . . . . . . 23
13. What This Is Not . . . . . . . . . . . . . . . . . . . . . . 25
14. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 26
15. References . . . . . . . . . . . . . . . . . . . . . . . . . 26
15.1. Normative References . . . . . . . . . . . . . . . . . . 26
15.2. Informative References . . . . . . . . . . . . . . . . . 27
Changes from -00 . . . . . . . . . . . . . . . . . . . . . . . . 28
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 30
Borthwick Expires 31 March 2027 [Page 2]
Internet-Draft Wallet State Attestation September 2026
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 30
1. Introduction
1.1. Motivation
Existing access-control primitives --- OAuth 2.0 [RFC6749], OIDC
[OIDC-CORE], SAML, and the broader credential-presentation family ---
answer the question "who is this user?" by accepting a presented
artifact (token, assertion, credential) from a holder.
Wallet State Attestation answers a different question: "does this
wallet currently satisfy a specified condition over on-chain state?"
The holder presents nothing. An issuer reads canonical on-chain
state for a specified wallet address, evaluates the operator-defined
condition, and returns a signed boolean. Verifiers check the
signature offline using a published JWKS endpoint.
This is a distinct primitive from identity verification, credential
issuance, or holder-presentation flows. It belongs in a parallel
category --- what this document terms wallet-state-attestation ---
alongside, not within, the existing credential-presentation stack.
1.2. Position Relative to Identity Primitives
OAuth and OIDC prove who a subject is. Wallet State Attestation
proves what a wallet currently holds, owns, or has been attested to
in on-chain state.
These are complementary, not competing. A system may use OAuth to
authenticate a human or service principal, then call a wallet-state
attestation issuer to verify that a wallet associated with that
principal satisfies an on-chain condition. The two primitives
operate on different subjects (identity vs. wallet state) and answer
different questions.
1.3. Position Relative to Credentials
Wallet State Attestation is *not* a Verifiable Credential (W3C VC)
[VC-DATA-MODEL], an mDoc / ISO/IEC 18013-5 mobile document, an eIDAS
attestation, a SAML assertion, or an OIDC ID token. These are
credential-presentation primitives: an authority issues a credential,
the holder presents it, the verifier checks the issuer's signature
over the holder's presented bytes.
Borthwick Expires 31 March 2027 [Page 3]
Internet-Draft Wallet State Attestation September 2026
Wallet State Attestation does not involve credential presentation.
The holder does not present anything. The issuer pulls public on-
chain state directly, evaluates a condition, and signs the result.
The signed payload is an observation about external state, not a
credential about the holder's identity.
This document treats the credential / VC / eIDAS / SAML stack as
adjacent prior art for explicit non-conflation purposes. See
Section 13 ("What This Is Not") for the full enumeration.
1.4. Position Relative to Other Documents
Wallet State Attestation is referenced or adopted in the following
public artifacts (all informative; see References):
* *Knowledge Context Protocol (KCP) RFC-0004* [KCP-0004] --- defines
an attestation_url / attestation_jwks mechanism in KCP YAML
manifests for verifying agent eligibility before access. The
pattern is mechanism-agnostic; wallet state attestation is one
valid implementation shape.
* *Verifiable Intent (VI) environment.* constraint family*
[VI-ENVIRONMENT] --- defines an environmental constraint family
for autonomous-mode (L3) execution in agent commerce, establishing
family-wide vocabulary (attestation_url, max_attestation_age),
composition rules, and IANA registry mechanics for individual
constraint types. The wallet-state-attestation primitive is the
reference implementation shape used by VI's
environment.wallet_state constraint type.
* *Agent Governance Vocabulary* [AGV] --- defines wallet_state as a
foundation-layer signal type with named production issuers.
* *Open Agent Trust Registry (OATR)* [OATR] --- third-party trust
registry of attestation issuers, including wallet-state-
attestation issuers.
* *Draft ERC-8210 Agent Assurance Protocol* [ERC-8210] --- proposed
IRiskHook verification taxonomy identifies wallet state
attestation as the representative shape for pre-commitment
eligibility verification (one of three categories in the
verification framework, alongside post-hoc resolution evidence and
behavioral reputation).
* *Universal Commerce Protocol (UCP) identity-linking*
[UCP-IDENTITY] --- wallet attestation acknowledged as a reserved
extensibility point for future non-OAuth identity-mechanism types.
Borthwick Expires 31 March 2027 [Page 4]
Internet-Draft Wallet State Attestation September 2026
Wallet state attestation is *not* dependent on or normatively related
to any of these documents. They are listed as evidence of adoption
and as informative context for readers seeking to understand where
the primitive is in use.
1.5. Use Cases
Common deployments of wallet state attestation include:
* *Condition-based access* --- gating API access, content access, or
commerce flows on whether a specified wallet holds a specified
token, NFT, or attested status at request time, without exposing
actual balance or transaction history.
* *Agent-to-agent trust verification* --- autonomous agents
verifying counterparty wallet state (holdings, attested compliance
status, etc.) before transaction execution.
* *Compliance gating* --- verifying on-chain compliance attestations
(KYC status from external attestors, country attestations,
sanctions clearance) against a wallet without the verifier
receiving any identity attribute beyond the boolean it asked for
(Section 11.3).
* *Pre-commitment eligibility* --- agent commerce protocols
verifying that an agent wallet satisfies pre-conditions
(sufficient stake, sufficient balance, attested status) before
contract commitment.
The primitive is read-only and does not interact with chain state
beyond observation. Implementations issue attestations on demand;
attestations are short-lived and expire (typically thirty minutes;
see Section 5).
2. Terminology
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 BCP
14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
The following terms are used throughout this document:
*Issuer* --- The party that reads on-chain state, evaluates
conditions, and signs the resulting attestation. The issuer is the
sole signer of attestations within its declared scope (see
Section 10.1).
Borthwick Expires 31 March 2027 [Page 5]
Internet-Draft Wallet State Attestation September 2026
*Holder* --- The wallet whose on-chain state is observed. The holder
does not present anything in this primitive; the issuer reads chain
state directly. The holder need not be aware of any individual
attestation.
*Verifier* --- The party that consumes the attestation, checks the
signature against the issuer's published JWKS, and acts on the
result.
*Condition* --- An operator-defined predicate over wallet state.
Examples include: "balance of token T at address A is greater than or
equal to value V", "wallet A owns NFT N", "wallet A has a valid on-
chain attestation of type T from attestor X".
*Attestation* --- The signed payload returned by the issuer in
response to a request. Distinguish explicitly from a credential
(which is presented by a holder), from a JWT claim in the credential
sense (which carries identity information), and from a VC (which is
issued by an authority over a holder's presented identity). An
attestation in this document's sense is an issuer-signed observation
about public on-chain state.
*Fact profile* --- A multi-condition, multi-dimension attestation
payload consisting of a structured collection of condition results,
organized by dimension and covered by a single issuer signature. A
fact profile is signed as one object under the algorithms of this
document; its detailed format is not specified here. Used for richer
wallet-state queries where a single boolean is insufficient.
Distinguish explicitly from a "trust score" or "reputation rating"
--- a fact profile contains observed evidence organized by dimension,
not an opinion or aggregate.
*Block reference* --- Chain-specific freshness evidence included in
every attestation. EVM chains MUST include block number and block
timestamp. XRPL MUST include ledger index and, where available,
ledger hash. Solana MUST include slot. Bitcoin MUST include block
height and block hash (chain-tip evidence; see Section 9 for Bitcoin-
specific considerations). Any other chain family MUST include the
reference the issuer documents for it.
*Condition hash* --- A SHA-256 hash of the JSON serialization of the
evaluated condition, with member names sorted as [RFC8785] sorts them
and no insignificant whitespace, represented as a 0x-prefixed
lowercase hexadecimal string, included in the response for tamper-
evidence over the condition itself.
Borthwick Expires 31 March 2027 [Page 6]
Internet-Draft Wallet State Attestation September 2026
3. Architecture Overview
The primitive operates as a three-step pipeline: *read -> evaluate ->
sign*.
1. The issuer receives a request specifying a wallet address, a
chain identifier, and one or more conditions.
2. The issuer reads canonical on-chain state for the specified
wallet at or near the current chain tip. The issuer selects the
block and reports it in the block reference.
3. The issuer evaluates each condition against the observed state.
4. The issuer signs the result and returns the attestation.
The evaluation is *deterministic*: the same condition evaluated
against the same chain state at the same block produces the same per-
condition result. A verifier can independently re-derive each result
by reading the same chain state and applying the same condition
logic, exactly where the block reference names the state that was
read (an EVM block, an XRPL ledger) and to within the anchor where
the reference is a tip marker or a floor, as the issuer documents per
chain family. The attestation identifier, the attestedAt timestamp,
and the signature value differ from one issuance to the next.
The primitive is *read-only*: the issuer does not modify chain state,
does not request a wallet signature from the holder, and does not
custody any assets. The issuer is a passive reader of public chain
state.
For each chain referenced in the request, the issuer captures a block
reference (block number and block timestamp for EVM chains; ledger
index and, where available, ledger hash for XRPL; slot for Solana;
block height and block hash for Bitcoin; for any other chain family,
the reference the issuer documents) at attestation time. The block
reference is carried inside each per-condition result and is
therefore covered by the issuer's signature. If a required chain
observation cannot be acquired, the issuer MUST NOT sign the
attestation; see Section 10.3.
This document does not specify how the issuer reads chain state (RPC
routing, indexer choice, archive node selection), how it manages
signing keys (HSM, software, federated), or how it handles chain-
specific edge cases (reorgs, finality, data availability). These are
implementation concerns, deliberately left out of scope.
Borthwick Expires 31 March 2027 [Page 7]
Internet-Draft Wallet State Attestation September 2026
4. Request Shape
A request is a JSON object with the following structure:
{
"wallet": "
",
"conditions": [
{
"type": "",
"chainId": "",
"...": ""
}
],
"format": "jwt",
"proof": "merkle"
}
*wallet* --- A chain-specific address string. EVM addresses use the
standard 0x-prefixed hexadecimal form. Solana addresses use base58.
XRPL addresses use the r-address format. Bitcoin addresses MAY use
any standard format (P2PKH, P2SH, bech32 / SegWit, bech32m /
Taproot). Because one request can span several chains, an
implementation MAY reserve wallet for one address family and carry
the others in chain-specific members (e.g., solanaWallet, xrplWallet,
bitcoinWallet).
*chainId* --- A chain identifier, carried by each condition whose
type is not bound to a single chain, so that one request can span
several chains. EVM chains use the numeric EIP-155 chain identifier
(the reference part of a CAIP-2 identifier [CAIP-2]), expressible as
integer (e.g., 1 for Ethereum mainnet) or string. Non-EVM chains use
a string identifier naming the chain (for example solana, xrpl,
bitcoin).
*conditions* --- A list of one or more condition objects. Each has a
type field discriminating the condition category, and type-specific
fields specifying the parameters of that category. This document
does not enumerate all possible condition types. Common categories
include token balance evaluation, NFT ownership evaluation, on-chain
attestation reference (e.g., reference to an Ethereum Attestation
Service entry), and identity-registry membership evaluation.
Implementations MAY define additional condition types.
*format* --- OPTIONAL. When "jwt", the response additionally carries
the attestation as an RFC 7519 JWT (Section 8). When omitted, the
response is the JSON format of Section 5 alone.
Borthwick Expires 31 March 2027 [Page 8]
Internet-Draft Wallet State Attestation September 2026
*proof* --- OPTIONAL. Requests a cryptographic proof layer. When
omitted, no proof is requested. Value: "merkle" (where supported by
the underlying chain; returns [EIP-1186] Merkle storage proofs
anchored to the block header).
5. Response Shape
A response contains a signed attestation object together with the
signature and key identifier. The signed attestation object has the
following structure:
{
"id": "",
"pass": true,
"results": [
{
"condition": 0,
"label": "",
"type": "",
"chainId": "",
"met": true,
"evaluatedCondition": { /* condition object */ },
"conditionHash": "0x",
"blockNumber": "",
"blockTimestamp": ""
}
],
"attestedAt": ""
}
The signature is computed over signed bytes derived from this object
under the signing scheme that the transmitted kid selects
(Section 6). The signature, the key identifier (kid), expiry
timestamp (expiresAt), and any deployment-specific wrappers are
transmitted alongside the signed object but are NOT part of the
signed bytes. Nor is any member of the transmitted attestation
object other than id, pass, results, and attestedAt: an issuer MAY
add informational members there (for example, counts of met and unmet
conditions), and they are unsigned. Note that the signed object does
not in general name the wallet: the requester knows which wallet it
asked about, but a party that receives a JSON-format attestation
second-hand cannot bind it to a wallet from the signed bytes alone
(some condition types carry the address inside evaluatedCondition).
The JWT format names the wallet in its sub claim (Section 8). A
typical wire response carries the signed object plus these adjacent
fields:
Borthwick Expires 31 March 2027 [Page 9]
Internet-Draft Wallet State Attestation September 2026
{
"attestation": {
/* signed object above */
"expiresAt": ""
},
"sig": "",
"kid": "",
"pqSig": "",
"pqKid": ""
}
Field semantics (signed object):
*id* --- An opaque attestation identifier. Implementation-specific
format. Useful for correlation, logging, and replay detection. It
is covered by the signature.
*pass* --- The aggregate result. Default semantics: logical AND of
all per-condition met values. Operators MAY specify alternate
combination logic in extensions.
*results* --- An array of per-condition result objects. Each MUST
include the per-condition met boolean, the condition type, the
verbatim evaluatedCondition (as it was evaluated, after any
normalization), and the conditionHash (SHA-256 of the UTF-8 JSON
serialization of evaluatedCondition with member names sorted as
[RFC8785] sorts them (by UTF-16 code units) and no insignificant
whitespace, as a 0x-prefixed lowercase hexadecimal string). The
evaluatedCondition object is flat: its member values are strings,
numbers, booleans, or null, never objects or arrays, so that sorting
its member names fully determines the serialization; strings and
numbers are serialized as in the bare scheme of Section 6.1. A
verifier MUST recompute conditionHash from the transmitted
evaluatedCondition and MUST reject the result as altered if the two
differ. Each result MUST include chain-specific freshness evidence:
blockNumber and blockTimestamp for EVM chains; ledgerIndex and (where
available) ledgerHash for XRPL; slot for Solana; blockHeight and
blockHash for Bitcoin; for any other chain family, the reference the
issuer documents for it. All such freshness fields are inside the
signed results array and are therefore covered by the issuer's
signature. Implementations MAY include additional informational
fields (condition index, label, etc.) echoed from the request.
*attestedAt* --- ISO 8601 timestamp of when the attestation was
signed.
Field semantics (adjacent, unsigned):
Borthwick Expires 31 March 2027 [Page 10]
Internet-Draft Wallet State Attestation September 2026
*expiresAt* --- ISO 8601 timestamp of when the attestation expires.
Verifiers SHOULD check expiry (step 6 of Section 12). Recommended
default: thirty minutes after attestedAt. Implementations MAY use
shorter expiries; longer expiries SHOULD be justified by the use
case. expiresAt is transmitted with the attestation but is not part
of the signed payload; verifiers concerned with tamper-evidence over
the expiry SHOULD use the JWT format (Section 8), where exp is a
signed claim.
*kid* --- Key identifier (RFC 7517) for the public key used to sign
this attestation. Verifiers use this to look up the appropriate
public key in the issuer's JWKS. kid is transmitted alongside the
signed payload but is not part of the signed bytes. The kid selects
both the verification key and the signing scheme (Section 6.1);
verifiers resolve it against the issuer's published JWKS as described
in Section 7. The JWT format embeds kid in the protected header
where it is covered by the JWT signature.
*sig* --- The signature. Default: ECDSA P-256 signature over the
signed bytes defined in Section 6, in P1363 (raw R || S) format,
base64-encoded with the standard alphabet and padding (Section 4 of
[RFC4648]).
*pqSig*, *pqKid* --- OPTIONAL. A companion signature under a second
algorithm and the identifier of the key that produced it
(Section 6.3). When present they are additive: sig and kid are
unchanged by their presence, and a verifier that does not implement
companion verification ignores them.
The signature scope includes the evaluatedCondition and conditionHash
fields nested inside results, ensuring the signature is tamper-
evident over both the result and the condition that produced it.
6. Signing Algorithm
The default signing algorithm is *ECDSA with P-256 curve and SHA-256
(ES256)* per [RFC7518].
The signed bytes are produced under one of the signing schemes in
Section 6.1. An issuer MAY additionally attach a companion signature
under a second algorithm (Section 6.3).
Borthwick Expires 31 March 2027 [Page 11]
Internet-Draft Wallet State Attestation September 2026
6.1. Signing Schemes
This document defines two signing schemes. The type of message a
verifier holds is evident from its shape (an attestation of Section 5
carries pass and results; a fact profile does not); the kid
transmitted with it selects both the verification key and the signing
scheme. The issuer's documentation states, for each kid it
publishes, which scheme that kid selects and, for the domain-
separated scheme, the domain tag. Verifiers MUST determine the
scheme from the kid, MUST NOT assume that an issuer uses a single
scheme, and MUST NOT verify an attestation under any scheme other
than the one its kid selects. A single public key MAY be published
under more than one kid, so that one key serves more than one scheme.
An issuer that signs more than one type of message under the domain-
separated scheme MUST use a distinct domain tag for each type, and a
verifier MUST NOT accept an attestation under a kid that the issuer
documents for a different type of message.
Signing schemes apply to the JSON format of Section 5. In the JWT
format (Section 8) the signed bytes are the JWS Signing Input
[RFC7515] of the JWT [RFC7519], whatever scheme the kid would select
in the JSON format; there the kid selects the verification key (and,
through its JWKS entry, the algorithm) but no scheme.
*Bare scheme.* The signed bytes are the JSON serialization of an
object carrying exactly the four members of the signed object, in the
order id, pass, results, attestedAt, with no insignificant
whitespace, encoded as UTF-8. Inside results, every member of every
result object, including the members of evaluatedCondition, is
serialized in the order in which it was transmitted, and array
element order is preserved. Nothing inside the signed bytes of this
scheme is re-sorted: the sorting of member names described in
Section 5 applies only to the computation of conditionHash. Strings
and numbers are serialized as the ECMAScript JSON.stringify function
serializes them, which for well-formed Unicode strings and finite
numbers is also the serialization of primitive values specified by
[RFC8785].
The signed bytes are not transmitted as such; a verifier reconstructs
them from the transmitted response. Under the bare scheme a verifier
therefore MUST use a JSON parser that preserves the order of object
members, and MUST omit from the reconstruction every member of the
transmitted attestation object other than the four named above.
*Domain-separated scheme.* The signed bytes are the concatenation of
(1) a domain tag, (2) a single line feed character (U+000A), and (3)
the canonical serialization of a JSON object carrying the member v
(the scheme version, the integer 2 for the scheme defined here)
Borthwick Expires 31 March 2027 [Page 12]
Internet-Draft Wallet State Attestation September 2026
together with the four members of the signed object (id, pass,
results, attestedAt). The canonical serialization is the JSON
Canonicalization Scheme [RFC8785]: member names sorted at every level
of nesting, array element order preserved, no insignificant
whitespace, and strings and numbers serialized as that document
specifies. (RFC 8785 requires well-formed Unicode strings; an issuer
MUST NOT sign a string that is not.) Because the member names are
sorted, a verifier needs no order-preserving parser under this
scheme. The domain tag is an ASCII string, fixed by the issuer, that
identifies the message type and the scheme version. It binds a
signature to its message type, so that a signature produced over one
type of message cannot be presented as a signature over another. An
issuer using this scheme SHOULD carry numeric quantities inside the
signed object (thresholds, amounts, ratios) as decimal strings and
SHOULD confine JSON numbers to small integers, so that the signed
bytes do not depend on any implementation's floating-point number
formatting and can be reproduced exactly in any language. The signed
bytes are encoded as UTF-8.
Signed bytes are stable per kid. An issuer introducing a new scheme
MUST do so under a new kid and MUST NOT change the signed bytes
produced under a kid it has already published. Attestations signed
under an earlier scheme therefore remain verifiable, unchanged, by
verifiers deployed before the new scheme existed.
6.2. Signature Encoding and Additional Algorithms
The signature output is the raw P1363 (R || S) representation of the
ECDSA signature, base64-encoded (88 characters for P-256).
Implementations producing DER-encoded ECDSA signatures MUST convert
to P1363 before transmission.
Implementations MAY support additional algorithms (ES384, EdDSA /
Ed25519). Algorithm selection is encoded in the JWKS entry indexed
by kid. Verifiers MUST NOT verify a signature under an algorithm
other than the one the selected JWKS entry implies (its alg, and the
key type and curve it carries).
6.3. Companion Signatures
An issuer MAY attach to an attestation a companion signature produced
under a second algorithm, for example a post-quantum signature
algorithm. A companion signature is additive: it changes neither the
signed object, nor the signed bytes defined in Section 6.1, nor the
values of sig and kid. A verifier that does not implement companion
verification ignores the companion fields and is otherwise
unaffected.
Borthwick Expires 31 March 2027 [Page 13]
Internet-Draft Wallet State Attestation September 2026
In the JSON format the companion is carried in two adjacent fields:
pqSig, the companion signature, base64-encoded with the standard
alphabet and padding (Section 4 of [RFC4648]), and pqKid, the key
identifier of the companion key in the issuer's JWKS. The companion
signature is computed over the concatenation of (1) a companion
domain tag, (2) a single line feed character (U+000A), and (3) the
exact signed bytes that the attestation's kid selects under
Section 6.1, encoded as UTF-8. A verifier therefore reuses the bytes
it has already reconstructed for the primary signature. The
companion domain tag is distinct from any domain tag used by a
signing scheme and is documented by the issuer for each pqKid it
publishes.
The companion algorithm defined by this document is ML-DSA-65
[FIPS204]. Signatures are produced as in Algorithm 2 of [FIPS204]
(ML-DSA.Sign, not the pre-hash variant) with an empty context string;
these are the parameters that [RFC9964] specifies for JOSE. The
companion key is published in the issuer's JWKS (Section 7 of this
document) as an AKP key, the key type defined by [RFC9964].
In the JWT format (Section 8) the companion is a sibling token,
pqJwt, returned beside the ES256 JWT: a JWS in compact serialization
[RFC7515] whose protected header carries alg ML-DSA-65 and the
companion kid, and whose claims are the same as those of the ES256
JWT. No companion domain tag is used in the JWT format: pqJwt is
verified over its own JWS signing input. A verifier that checks
pqJwt MUST also confirm that its payload and the ES256 JWT's payload
carry the identical claim set: the same member names and, for every
member, a deeply equal JSON value (objects compared without regard to
member order, arrays in order, numbers by value). Any difference,
whether a changed value, a missing member, or an extra member on
either side, makes the companion refuted. Byte-identical payload
segments satisfy this trivially, but a verifier MUST NOT require
byte-for-byte equality, since two serializers may order members
differently. The companion then vouches for every claim a relying
party reads from the ES256 JWT, and cannot be transplanted from
another attestation.
Borthwick Expires 31 March 2027 [Page 14]
Internet-Draft Wallet State Attestation September 2026
A verifier that implements companion verification MUST report the
companion result as a verdict separate from the primary signature
result, with one of four values: _verified_ (transmitted, and
verifies under the key that pqKid selects; in the JWT format, also
carrying the same claim set as the ES256 JWT); _refuted_
(transmitted, and either fails verification under that key or, in the
JWT format, carries a different claim set); _absent_ (no companion
was transmitted, that is, no pqSig in the JSON format and no pqJwt in
the JWT format); or _unverifiable_ (transmitted, but the verifier
could not check it). Unverifiable records a gap in the verifier's
knowledge; refuted records positive evidence of alteration. A
verifier determines the verdict by the first of these that applies:
1. No companion was transmitted: absent.
2. The attestation's own kid is missing, or is one the verifier has
never met, so the signed bytes the companion covers cannot be
reconstructed: unverifiable. A verifier MUST NOT substitute the
signed bytes of another scheme for a kid it does not know, since
that could only produce a false refutation.
3. pqKid resolves to no JWKS entry, resolves to a key of the wrong
type, or names a companion key the issuer documents for a
different type of message; or pqSig is transmitted without pqKid;
or the verifier has no implementation of the algorithm:
unverifiable. A verifier applies no companion domain tag other
than the one pqKid names, so a pqKid of the wrong type leaves it
nothing it is entitled to check.
4. Otherwise the companion signature is checked, and in the JWT
format the claim sets are compared: verified or refuted. An
attestation whose own kid the verifier knows but the issuer
documents for a different type of message reaches this step with
well-defined signed bytes, and its companion is checked against
them; a relabelled artifact is therefore never unverifiable.
Item 2, and the relabelling case of item 4, concern the signed bytes
of the JSON format. In the JWT format the companion is a JWS over
its own signing input and is bound to the ES256 JWT by its claim set,
so it is evaluated whether or not the JWT's kid selects a key; a
genuine pqJwt beside a JWT whose kid the verifier cannot place is
reported verified while the JWT's own signature verdict is
unverifiable.
In the JWT format, a pqJwt that does not parse as a compact JWS, or
whose protected header lacks either the companion algorithm or a kid,
is malformed and is refuted, since the header is covered by the
companion signature and the issuer always emits both; this rule is
Borthwick Expires 31 March 2027 [Page 15]
Internet-Draft Wallet State Attestation September 2026
applied before item 3 of the list. A refuted companion MUST cause
the attestation to be rejected. An unverifiable companion MUST NOT
be treated as verified. Whether an absent or unverifiable companion
causes rejection is a matter of verifier policy: a verifier MAY
require a verified companion from a date of its own choosing, and it
MUST judge that date by its own clock at the time of verification,
never by a timestamp carried in the attestation. A verifier reading
an attestation after the fact, as a record rather than for a live
decision, MUST NOT refuse it for lacking a companion; it reports the
absence.
7. JWKS Discovery
The issuer publishes its public key set at a discoverable HTTPS
endpoint following [RFC7517]. A commonly used path is /.well-known/
jwks.json, under the well-known URI prefix of [RFC8615]; this
document does not register that path.
Each JWKS entry MUST include kid, kty, and alg, together with the
public key parameters that its key type requires: crv, x, and y for
an elliptic curve key (kty EC, [RFC7518]); pub for an Algorithm Key
Pair key (kty AKP, [RFC9964]); and correspondingly for any other key
type. A JWKS MAY therefore hold keys of more than one key type, and
verifiers MUST NOT require the parameters of one key type on an entry
of another. Issuers MAY publish multiple active keys simultaneously,
to support key rotation, more than one signing scheme (Section 6.1),
or companion signatures (Section 6.3). An issuer adding entries of a
new key type to an existing JWKS SHOULD append them after the
existing entries, so that the position of every existing entry is
unchanged for deployed verifiers.
Key selection: verifiers MUST select the verification key by the
transmitted kid, never by position in the JWKS and never by default.
If the kid is missing, or resolves to no entry in the JWKS available
to the verifier, the verifier MUST NOT accept the attestation and
MUST NOT substitute any other key, including the first entry in the
JWKS or a key built into the verifier. Such an attestation is
_unverifiable_, which is distinct from _refuted_: a refuted
attestation carries a signature that fails under the key its kid
selects, whereas nothing has been shown about the signature of an
unverifiable one. Verifiers SHOULD report the two outcomes
distinctly. Neither outcome is a successful verification.
Key rotation: when the issuer rotates signing keys, the new public
key MUST be published in JWKS before the issuer signs any attestation
with the corresponding private key. Retired public keys SHOULD
remain published in JWKS for a duration at least equal to the longest
possible outstanding attestation expiry (typically the default
Borthwick Expires 31 March 2027 [Page 16]
Internet-Draft Wallet State Attestation September 2026
thirty-minute window). An issuer MAY instead retain every key it has
ever published, under its original kid, so that an attestation kept
as a record remains checkable for as long as anyone holds it;
retention does not extend an attestation's validity, which its expiry
still governs. Relying parties that keep attestations as records
SHOULD keep a copy of the JWKS with them.
Because JWKS documents are cached, by verifiers and by
intermediaries, different verifiers can hold different versions of an
issuer's JWKS for up to the cache lifetime after it changes. During
that interval a verifier holding the earlier version will find that a
newly introduced kid resolves to no entry; the attestation is then
unverifiable, not refuted, as described above. A verifier that meets
an unknown kid MAY refresh its copy of the JWKS and retry.
Verifiers SHOULD cache the JWKS according to the HTTP cache headers
the issuer publishes, and MAY be supplied with a copy of the JWKS out
of band instead of fetching it. The cache lifetime an issuer sets
bounds how quickly a newly published kid becomes visible to
verifiers; an issuer SHOULD document it.
8. JWT Format
When format is "jwt", the response additionally carries the
attestation as an RFC 7519 JWT, in a jwt member beside the members of
the JSON format (Section 5).
Protected header: {"alg": "ES256", "typ": "JWT", "kid": ""}
per [RFC7519]. The kid in the protected header is covered by the JWT
signature.
Standard claims:
* iss --- issuer identifier (e.g., the issuer's base URL)
* sub --- the wallet address a condition in the request evaluated.
When the conditions span several address families, the issuer
documents which family's wallet sub names; a wallet the request
supplied but no condition evaluated is not named.
* jti --- JWT identifier (typically the attestation id)
* iat --- issuance timestamp (Unix seconds)
* exp --- expiry timestamp (Unix seconds)
Custom claims carrying the attestation payload:
Borthwick Expires 31 March 2027 [Page 17]
Internet-Draft Wallet State Attestation September 2026
* pass --- aggregate result
* results --- per-condition result array
* conditionHash --- array of per-condition conditionHash values, in
results order
* blockNumber --- chain block reference (typically the first
result's blockNumber, where chain-homogeneous)
* blockTimestamp --- chain block timestamp (typically the first
result's blockTimestamp)
The JWT signature is computed by the issuer using the corresponding
private key. Verifiers verify using any standard JWT library that
supports ES256 and JWKS discovery. When the issuer attaches a
companion signature, the JWT format carries it as the sibling token
pqJwt (Section 6.3); the ES256 JWT is unchanged by its presence.
The JWT format provides signed coverage of exp and kid (via standard
claim and protected header, respectively); deployments requiring
tamper-evident expiry or signed key-identifier binding SHOULD use the
JWT format rather than the JSON format described in Section 5.
A response in the JWT format carries the token beside the JSON-format
members, so a verifier may hold both. A relying party that reads
claims from the JWT MUST have verified the JWT itself; verifying the
JSON-format attestation beside it is not a substitute, because the
two are separately signed. A verifier handed both SHOULD verify the
JWT as well as the attestation, and report the JWT's outcome as its
own verdict: its signature under the same kid as the response, its
results under step 4 of Section 12, its companion under Section 6.3,
and that it is the token of this attestation (its kid equals the
response's kid, jti equals id, and pass, results, and exp equal the
attestation's pass, results, and expiresAt truncated to whole
seconds). A transmitted JWT that fails any of these rejects the
response: a relying party cannot tell which of the two it will be
shown. The correspondence check does not cover sub, which the JSON-
format attestation does not carry; the wallet named in sub is vouched
for by the JWT's own signature alone. A JWT MAY also be presented on
its own; it is then a complete attestation, verified as Section 12
describes for the JWT format, without the correspondence check.
Borthwick Expires 31 March 2027 [Page 18]
Internet-Draft Wallet State Attestation September 2026
9. Optional Merkle Proofs
When proof is "merkle" and the underlying chain supports Merkle
storage proofs (typically EVM chains for storage-slot-mappable
conditions), the response MAY include [EIP-1186] Merkle storage
proofs anchored to the block header for each per-condition result.
Merkle proofs permit trustless verification: a verifier can
independently check the Merkle proof against the block reference
without trusting the issuer's evaluation. When a Merkle proof is
included (proof.available is true in the per-result proof object),
the proof object MUST include the block reference and the proof nodes
needed to check the proven value against that block's state root (for
a storage-slot proof, including the storage slot path).
Not all conditions or chains support Merkle proofs. Implementations
MUST indicate proof availability per result and MAY return
proof.available: false with a reason field when proof generation is
unsupported for the condition or chain (e.g., NFT ownership
conditions, non-storage-slot-mappable conditions, non-EVM chains).
*Privacy trade-off:* Merkle proofs reveal raw on-chain values (e.g.,
the actual token balance). When no proof is requested, the wallet's
balance is not returned. Operators MUST consider whether the
trustlessness benefit of Merkle proofs is worth the privacy cost in
each deployment.
Bitcoin, XRPL, and Solana have different state-proof models and may
not support Merkle proofs in the [EIP-1186] sense. Implementations
targeting those chains MAY define chain-appropriate proof shapes or
MAY return proof.available: false.
For Bitcoin specifically: the UTXO model does not permit reproducible
balance-at-block queries. Bitcoin implementations anchor the
attestation to the chain tip (block height + block hash) at
observation time as the cryptographically-bound freshness evidence
appropriate to the UTXO model. The blockHeight and blockHash are
covered by the issuer's signature inside the result and bind the
observation to a recent chain tip rather than to a specific block's
historical state. Issuers MUST refuse to sign Bitcoin attestations
when the chain-tip lookup fails; see Section 10.3.
10. Security Considerations
Borthwick Expires 31 March 2027 [Page 19]
Internet-Draft Wallet State Attestation September 2026
10.1. Single-Issuer Architecture
This document describes a single-issuer attestation primitive. The
issuer is the sole signer of attestations within its declared scope.
This is a deliberate design choice for several reasons:
1. *Deterministic signature surface.* Verifiers can independently
re-derive an attestation's results from chain state without
resolving issuer trust hierarchies, federation policies, or
multi-party signing logic.
2. *No re-signing or wrapping.* This document defines no re-signing,
aggregation under a wrapper signature, or repackaging of
attestations by any party other than the issuer; a wrapper or
aggregate signature confers no authority, and a verifier accepts
no signature other than the issuer's own (its primary signature,
and any companion or JWT signature the issuer itself produced,
Section 6.3 and Section 8). The original observation's signature
is the authoritative artifact end-to-end.
3. *Unambiguous attribution.* For audit, dispute resolution, and
accountability, the issuer is unambiguously identified by the
JWKS endpoint and signing key.
Multi-issuer federation, key delegation, cross-issuer attestation
aggregation, and threshold signing are explicitly out of scope for
this document. Future Internet-Drafts MAY define such mechanisms as
separate primitives, but they are distinct from the single-issuer
wallet-state attestation defined here.
10.2. Freshness Anchors and Replay Considerations
The cryptographically-bound freshness anchors are the block reference
inside each per-condition result and the attestedAt timestamp in the
outer signed payload (the iat claim in the JWT format). Both are
covered by the issuer's signature. Verifiers requiring strict
tamper-evident freshness derive their policy from these signed
anchors (e.g., "reject if attestedAt is older than thirty minutes",
or "reject if blockTimestamp is older than sixty seconds").
The expiresAt field is an issuer-provided TTL hint, transmitted
alongside the attestation but outside the signed payload in the JSON
format described in Section 5. Because expiresAt is unsigned in the
JSON format, a verifier that relies on it SHOULD confirm that it is
no later than the signed attestedAt plus the issuer's documented
validity window, and SHOULD reject the attestation as altered
otherwise. Deployments requiring tamper-evident expiry SHOULD use
the JWT format described in Section 8, where exp is a signed claim.
Borthwick Expires 31 March 2027 [Page 20]
Internet-Draft Wallet State Attestation September 2026
For replay-sensitive deployments, implementations MAY include a nonce
binding or audience binding in custom JWT claims (when the JWT format
is used). This document does not normatively specify these
extensions; deployments requiring replay protection beyond freshness
checking SHOULD define them explicitly in their integration profile.
10.3. Refuse-on-Observation-Failure
Issuers MUST refuse to sign an attestation when any required
observation fails: chain-tip lookup, balance query, on-chain
attestation registry lookup, or any other data source the condition
depends on. A successfully signed attestation therefore implies that
all underlying observations were acquired against a current chain
reference at attestation time. Partial or degraded observations MUST
NOT be signed.
Verifiers MAY cross-check the included block reference against an
independent chain source if the deployment requires it.
10.4. What This Primitive Does Not Protect Against
* *Chain-level reorganizations after attestation.* If a chain reorgs
after an attestation has been signed, the historical state
observed in the attestation may no longer be canonical. Operators
handling this concern SHOULD use chains with strong finality,
SHOULD use sufficiently old block references, or SHOULD include
reorg-handling logic in their verification flow.
* *Off-chain state changes.* This primitive observes on-chain state
only. It does not attest to off-chain identity, off-chain
agreements, or off-chain commitments.
* *Malicious issuer.* A compromised or malicious issuer could sign
false attestations. This is mitigated by: (a) JWKS publication of
signing keys, (b) optional Merkle proofs for trustless
verification (where supported), (c) condition hashes for tamper-
evidence over the evaluated logic, and (d) deterministic re-
derivation by independent verifiers.
10.5. Algorithm Agility
The default ES256 algorithm is widely supported and provides 128-bit
security. Implementations supporting additional algorithms MUST
include the algorithm in the JWKS entry indexed by kid. Verifiers
MUST check the algorithm before verifying.
Borthwick Expires 31 March 2027 [Page 21]
Internet-Draft Wallet State Attestation September 2026
Algorithm migration: when the cryptographic landscape changes (e.g.,
post-quantum migration), this document provides two paths, and an
issuer can use either or both. _Rotation_: the issuer publishes a key
for the new algorithm in JWKS under a new kid and begins signing with
it; verifiers that do not support the new algorithm can no longer
verify new attestations. _Composition_: the issuer continues to sign
with the existing algorithm and attaches a companion signature under
the new algorithm (Section 6.3). Composition leaves the existing
signature, its signed bytes, and its key unchanged, so deployed
verifiers that ignore unrecognized members keep working, and each
verifier adopts the new algorithm on its own schedule by choosing the
date from which it requires a verified companion. Composition is an
application of post-quantum/traditional hybrid signing, for which
[RFC9794] gives terminology. In the JSON format a companion
signature covers the same signed bytes as the primary signature, so
an attacker must defeat both algorithms to forge an attestation that
is accepted by a relying party requiring both signatures to verify.
In the JWT format the two tokens are signed separately, and the same
property holds when the verifier requires their claim sets to be
identical, as Section 6.3 requires: an attacker who defeats only the
primary algorithm then cannot alter any claim without the companion
refuting it. Under either path the protocol surface remains stable.
11. Privacy Considerations
11.1. Boolean by Default
The default response mode returns the result of condition evaluation,
not the wallet's balance. A verifier learns whether a wallet
satisfies a condition (e.g., "holds at least 100 USDC") without being
told the balance itself. An issuer MAY return public reference
values that the evaluation used (for example, a token's total
supply); it SHOULD NOT return a value from which the wallet's balance
can be derived unless the caller requested proof mode.
This is the privacy-preserving default and SHOULD be used unless
specific deployment requirements justify a less-private mode.
11.2. Merkle Mode Trade-Off
When proof is "merkle", raw on-chain values are revealed for
trustless verification. Operators MUST consider the privacy
implications of revealing actual balances or asset holdings in each
deployment.
Borthwick Expires 31 March 2027 [Page 22]
Internet-Draft Wallet State Attestation September 2026
11.3. No Identity Attributes
The primitive handles no identity attributes: no name, document,
contact, or account data is collected, signed, or transmitted. The
one identifier it handles is the wallet address (Section 11.4), which
is public on the chain but persistent, and so may be personal data
where it can be linked to a natural person. The signed output is a
boolean about a condition; where the condition itself concerns a
person (for example, a KYC or jurisdiction attestation issued by a
third party), the boolean relates to that person even though no
attribute is disclosed, and deployers should handle it as information
about that person. The results echo operator-provided text (label,
and the condition parameters in evaluatedCondition); operators SHOULD
NOT place identity attributes in them.
11.4. Wallet Address Handling
The wallet address is the subject of the query but is public
information on the underlying chain. Issuers SHOULD NOT log or
retain wallet addresses beyond what is required for service delivery,
abuse prevention, and operational integrity. Implementations
targeting privacy-conscious deployments SHOULD document their
address-handling policy.
11.5. Awareness of Observation
This primitive observes public chain state without holder
participation. The holder is not required to consent to nor be aware
of any individual attestation, consistent with the public nature of
blockchain state. Deployers operating in regulated or consumer-
facing contexts SHOULD consider whether their deployment context
requires additional transparency mechanisms; such mechanisms are out
of scope for this document.
12. Verification Procedure
This section collects the checks of the preceding sections into one
procedure for the JSON format, naming the verdict at each branch.
Steps 1 to 4 are REQUIRED; steps 5 and 6 are RECOMMENDED; step 7 is
OPTIONAL. A verifier SHOULD report each step's outcome separately
rather than as one boolean, so that a relying party can apply its own
policy to the recommended and optional steps.
1. *Select the key and scheme.* Read the transmitted kid. If it is
absent, or resolves to no entry in the JWKS available to the
verifier, stop: the signature verdict is _unverifiable_
(Section 7). Do not substitute any other key. If the kid
resolves but the issuer documents it for a different type of
Borthwick Expires 31 March 2027 [Page 23]
Internet-Draft Wallet State Attestation September 2026
message, stop: the artifact is relabelled, and the signature
verdict is _refuted_. Otherwise the kid selects the key, the
algorithm (the JWKS entry's alg and key type), and the signing
scheme with its domain tag (Section 6.1).
2. *Reconstruct the signed bytes* from the transmitted attestation
object under the selected scheme: under the bare scheme, exactly
the members id, pass, results, and attestedAt in that order,
every nested member in transmitted order, no whitespace, UTF-8;
under the domain-separated scheme, the domain tag, a line feed,
and the RFC 8785 serialization of the object {v, id, pass,
results, attestedAt} with v equal to the scheme version. Members
of the transmitted object other than the four named are not part
of the signed bytes.
3. *Verify the primary signature*: decode sig (standard base64,
P1363), and verify it under the selected algorithm over the
signed bytes. Failure is _refuted_; the attestation is rejected.
4. *Recompute each condition hash*: for every result, serialize
evaluatedCondition with member names sorted and no whitespace
(Section 5), hash it, and compare with the transmitted
conditionHash. A mismatch marks the result as altered; the
attestation is rejected.
5. *Check freshness* against the signed anchors (attestedAt and each
result's block reference) under the verifier's own policy
(Section 10.2), and reject identifiers already seen if replay
protection is required.
6. *Check expiry*: reject if the current time is past expiresAt,
and, because expiresAt is unsigned in this format, reject as
altered an expiresAt later than the signed attestedAt plus the
issuer's documented validity window (Section 10.2). A verifier
MAY allow a small clock skew.
7. *Check the companion*, if one is transmitted, in the order given
in Section 6.3, and report one of verified, refuted, absent, or
unverifiable as its own verdict. Refuted rejects the
attestation; absent and unverifiable are subject to the
verifier's own policy.
Borthwick Expires 31 March 2027 [Page 24]
Internet-Draft Wallet State Attestation September 2026
After a stop at step 1, the verifier reports the signature verdict it
reached and the companion verdict of Section 6.3; steps 4 to 6 need
no key: step 4 MUST, and steps 5 and 6 SHOULD, still be performed and
reported; a step the verifier did not perform is reported as not
performed, never as passed; a verifier whose report has no separate
state for an unperformed step reports it as failed with a reason that
says it was not performed. The attestation is rejected whenever the
signature verdict is not verified.
In the JWT format, steps 1 to 3 are performed by a JWS library over
the token's own signing input, with the library pinned to the
algorithm the selected JWKS entry implies rather than to the token's
own alg header (the kid still selects the key, and step 1's rules for
a missing, unknown, or wrong-type kid still apply), step 4 applies to
the results claim, expiry is the signed exp claim, and the companion
is pqJwt bound by the full claim set (Section 6.3). A verifier
holding both formats of the same attestation also verifies the token
beside the attestation as Section 8 describes.
Issuers SHOULD publish test vectors covering, at least: a genuine
attestation under each scheme they use; a tampered condition, a
tampered signature, and a tampered condition hash; a missing and an
unknown kid; and, where companions are used, a genuine, a tampered,
and a mislabelled companion. A verifier that reproduces the
published verdicts on such a set has evidence of interoperability
that reading this document alone cannot give.
13. What This Is Not
This section explicitly enumerates adjacent primitives that wallet
state attestation is *not*, to prevent conflation:
* *Not a Verifiable Credential (W3C VC) [VC-DATA-MODEL].* VCs are
issued by an authority, presented by a holder, and verified
against the holder's presented bytes. Wallet state attestations
are pulled by the issuer from public chain state without holder
presentation.
* *Not an mDoc or eIDAS attestation.* mDocs (ISO/IEC 18013-5) are
pre-issued, batched, and stored by the holder for later
presentation. Wallet state attestations are issued on demand, are
short-lived, and are not stored by the holder.
* *Not a SAML assertion or OIDC ID token.* These primitives carry
identity claims about a user. Wallet state attestations carry
observations about wallet state, with no identity attribute
(Section 11.3).
Borthwick Expires 31 March 2027 [Page 25]
Internet-Draft Wallet State Attestation September 2026
* *Not a reputation score, trust score, or credit rating.* A wallet
state attestation contains observed facts (boolean results,
optionally with structured fact profiles). It does not contain
opinions, scores, ratings, or aggregated judgments. A separate
primitive built on top of wallet state attestations might produce
a score; this primitive does not.
* *Not a settlement attestation, delivery attestation, or
transaction proof.* Wallet state attestation is read-only state
observation at observation time. It does not attest to action
confirmation, transaction completion, or delivery of goods or
services.
* *Not an oracle in the price-feed sense.* This primitive observes
specific wallet state on demand for a specific request; it does
not publish continuous data streams or aggregate market data.
These distinctions are essential to keeping wallet state attestation
as a distinct primitive in the agent-commerce, condition-based-
access, and verification ecosystems.
14. IANA Considerations
This document has no IANA actions.
15. References
15.1. Normative References
[FIPS204] National Institute of Standards and Technology, "Module-
Lattice-Based Digital Signature Standard", NIST FIPS 204,
DOI 10.6028/NIST.FIPS.204, August 2024,
.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
.
[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data
Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006,
.
[RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web
Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May
2015, .
Borthwick Expires 31 March 2027 [Page 26]
Internet-Draft Wallet State Attestation September 2026
[RFC7517] Jones, M., "JSON Web Key (JWK)", RFC 7517,
DOI 10.17487/RFC7517, May 2015,
.
[RFC7518] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518,
DOI 10.17487/RFC7518, May 2015,
.
[RFC7519] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token
(JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015,
.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, .
[RFC8785] Rundgren, A., Jordan, B., and S. Erdtman, "JSON
Canonicalization Scheme (JCS)", RFC 8785,
DOI 10.17487/RFC8785, June 2020,
.
[RFC9964] Prorock, M. and O. Steele, "ML-DSA for JSON Object Signing
and Encryption (JOSE) and CBOR Object Signing and
Encryption (COSE)", RFC 9964, DOI 10.17487/RFC9964, May
2026, .
15.2. Informative References
[AGV] "Agent Governance Vocabulary", n.d.,
.
[CAIP-2] "Chain Agnostic Improvement Proposal 2: Blockchain ID
Specification", n.d.,
.
[EIP-1186] "Ethereum Improvement Proposal 1186: RPC-Method to get
Merkle Proofs", n.d.,
.
[ERC-8210] "Draft Ethereum Improvement Proposal 8210: Agent Assurance
Protocol", n.d., .
[KCP-0004] "Knowledge Context Protocol RFC-0004: Trust, Provenance,
and Compliance", n.d., .
Borthwick Expires 31 March 2027 [Page 27]
Internet-Draft Wallet State Attestation September 2026
[OATR] "Open Agent Trust Registry", n.d.,
.
[OIDC-CORE]
Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., and
C. Mortimore, "OpenID Connect Core 1.0", n.d.,
.
[RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework",
RFC 6749, DOI 10.17487/RFC6749, October 2012,
.
[RFC8615] Nottingham, M., "Well-Known Uniform Resource Identifiers
(URIs)", RFC 8615, DOI 10.17487/RFC8615, May 2019,
.
[RFC9794] Driscoll, F., Parsons, M., and B. Hale, "Terminology for
Post-Quantum Traditional Hybrid Schemes", RFC 9794,
DOI 10.17487/RFC9794, June 2025,
.
[UCP-IDENTITY]
"Universal Commerce Protocol: Identity Linking", n.d.,
.
[VC-DATA-MODEL]
Sporny, M., "Verifiable Credentials Data Model", n.d.,
.
[VI-ENVIRONMENT]
Borthwick, D. and M. Msebenzi, "Verifiable Intent ---
environment.* Constraint Family", May 2026,
.
Changes from -00
This revision extends the format in two ways, restates the request
shape, and states the existing format more precisely. Nothing an
issuer signs changes: the signature over every attestation issued
under -00 verifies under this revision exactly as before. Where a
verifier built from this revision behaves differently from one built
from -00, the difference is listed in the last two items or, for the
added scheme and the correspondence check, in the item that
introduces it.
Borthwick Expires 31 March 2027 [Page 28]
Internet-Draft Wallet State Attestation September 2026
* Section 6 now has subsections and defines two signing schemes
selected by kid: the bare scheme and a domain-separated scheme
based on [RFC8785]. It states that signed bytes are stable per
kid, that one key may be published under several kid values, that
the schemes apply to the JSON format, that the signed bytes are
reconstructed from the transmitted object under the bare scheme
with every member in transmitted order, and that sorting of member
names applies to conditionHash alone.
* New Section 6.3 defines optional companion signatures under a
second algorithm (ML-DSA-65), carried as pqSig and pqKid in the
JSON format and as pqJwt in the JWT format, bound in the JWT
format by the full claim set, with four reported verdicts and the
order in which they are determined. Section 10.5 describes
composition alongside rotation as a path for algorithm migration.
* New Section 12 collects the checks into one numbered procedure,
names the verdict at each branch, states what is reported after a
stop, maps the procedure onto the JWT format, and recommends that
issuers publish test vectors.
* Section 7 states the required JWK members per key type, so that a
JWKS can hold elliptic curve and AKP keys together; the key
selection rule (by kid, never by position or default; a missing or
unknown kid is unverifiable, reported distinctly from refuted);
that an issuer may retain published keys permanently; guidance on
cached JWKS documents, including that a verifier MAY hold a copy
of the JWKS supplied out of band and MAY refresh and retry on an
unknown kid; and that new key types are appended after existing
entries. Caching is stated in terms of the issuer's published
headers, without a figure.
* Section 5 states that members of the transmitted attestation
object other than the four signed members are unsigned, that the
signed object does not in general name the wallet, that
evaluatedCondition is flat, the base64 alphabet, and the
serialization used for conditionHash. Section 4: chainId is
carried by each condition; format and proof are top-level members;
the chain-family lists are open-ended. Section 8: the JWT is
carried beside the JSON format; sub is the wallet a condition
evaluated; a relying party that reads claims from the JWT verifies
the JWT itself; a correspondence check between the two formats is
given.
* Statements made more precise: a fact profile is covered by a
single signature; the XRPL ledger hash is included where
available; the evaluation, rather than the attestation, is
deterministic, and re-derivation is exact where the block
Borthwick Expires 31 March 2027 [Page 29]
Internet-Draft Wallet State Attestation September 2026
reference names the state read; the issuer selects the anchored
block; the contents of a proof object are stated for proofs that
are not storage-slot proofs; id is signed and usable for replay
detection; iat is the JWT counterpart of attestedAt; format and
proof are omitted rather than set to "json" or "none"; checking
expiry is RECOMMENDED, consistent with Section 12. The Abstract
states that verification needs no contact with the issuer beyond a
copy of its published key set and documentation. Section 11.1
states that the default mode does not return the balance and may
return public reference values the evaluation used. Section 11.3
names the one identifier the primitive handles, the wallet
address, and how a boolean about a person is to be treated;
Section 1.5 is aligned with it. Section 10.1 states that a
wrapper signature confers no authority.
* References: added [FIPS204], [RFC4648], [RFC7515], [RFC8615],
[RFC9964] and [RFC9794]; [RFC8785] is normative; [RFC8615] takes
the place of RFC 5785; one informative URL is updated.
* Verifier behaviour that differs from a verifier built from -00: a
missing or unknown kid is not accepted and no other key is
substituted; a kid documented for a different type of message is
not accepted; conditionHash is recomputed, and a mismatch causes
rejection; a refuted companion causes rejection; a transmitted JWT
that fails verification or does not correspond to the attestation
beside it rejects the response; a verifier relying on the unsigned
expiresAt checks it against the signed attestedAt. In each case
the verifier accepts less.
* Requirements stated more loosely than in -00: checking expiry is
RECOMMENDED rather than required (Section 5, consistent with
Section 12); verifier caching of the JWKS is a SHOULD rather than
a MUST, and the -00 recommendation of a one-hour maximum cache
lifetime is not carried forward (Section 7); an XRPL ledger hash
is included where available rather than always (Section 2). The
issuer selects the anchored block; the -00 option of a caller-
specified block is not carried forward (Section 3). A verifier
built from this revision also verifies attestations under the
domain-separated scheme and accepts a JWKS that holds more than
one key type, neither of which a verifier built from -00 does.
Acknowledgments
The author thanks the IETF community for ongoing discussion of
attestation primitives in the agentic and wallet-state space.
Author's Address
Borthwick Expires 31 March 2027 [Page 30]
Internet-Draft Wallet State Attestation September 2026
Douglas Borthwick
InsumerAPI
New Canaan, CT
United States of America
Email: douglas@insumermodel.com
Borthwick Expires 31 March 2027 [Page 31]