Specification

Credential claim

RVW-1
StatusReview  Implemented and open to problems. Breaking changes are announced.
Version0.3
Updated9 Nov 2026
Depends onNothing
LicenceCC0 1.0. Implement it without asking.
01

Scope

A claim is the statement a professional makes about a credential they hold. This document defines the fields, the canonical form, the identifier and the signature. It does not define how a claim is stored, served or found, because the identifier is a digest and the document is therefore location independent.

02

The document

claim.jsonJSON
{
  "rvw": 1,
  "subject": "0xa805a67f59cccc9869d5dcb4ffc02fc697b5819d",
  "trade": "real-estate-agent",
  "jurisdiction": "AU-QLD",
  "authority": "au-qld-fair-trading",
  "licence": "4102938",
  "legal_name": "Northpier Property Pty Ltd",
  "domain": "northpier.example",
  "register": "https://register.example.qld.gov.au/licence/4102938",
  "issued": "2026-11-09T00:00:00Z",
  "expires": "2027-05-09T00:00:00Z"
}

Values are UTF-8 strings or integers. Floating point numbers are not allowed anywhere in a claim, which is what makes canonicalisation trivial and identical in every language.

03

Fields

FieldRequiredMeaning
rvwYesDocument type. Always the integer 1 for a claim.
subjectYesThe identity key making the statement, as a lowercase hex address.
tradeYesA lowercase trade identifier such as real-estate-agent or conveyancer. Free text, matched by consumers, not policed here.
jurisdictionYesISO 3166 country, optionally with a subdivision, such as AU-QLD or GB-SCT.
authorityYesAn identifier for the issuing body. See the limits in section 8.
licenceYesThe licence reference exactly as the authority writes it, including letters and leading zeros.
legal_nameYesThe registered name the licence is held under.
domainNoThe business domain used for the binding in RVW-2. Omit it if there is none.
registerNoA stable public address where this licence can be looked up, if one exists.
issuedYesRFC 3339 UTC. Not in the future.
expiresYesRFC 3339 UTC. At most twelve months after issued.

Unknown fields are preserved and included in the digest. A consumer ignores what it does not understand, and never drops a field before hashing.

Fields deliberately absent: identity document numbers, personal addresses, phone numbers, dates of birth, anything held by a human and not by a business. Rule five.

04

Canonical form

Object keys sorted by Unicode code point, recursively. No whitespace outside strings. UTF-8. No floats. That is the whole definition, and it is what the standard shell tools already produce:

terminalSHELL
jq -cS . claim.json | tr -d '\n' | shasum -a 256
canonical formTEXT
{"authority":"au-qld-fair-trading","domain":"northpier.example",
"expires":"2027-05-09T00:00:00Z","issued":"2026-11-09T00:00:00Z","jurisdiction":"AU-QLD",
"legal_name":"Northpier Property Pty Ltd","licence":"4102938",
"register":"https://register.example.qld.gov.au/licence/4102938","rvw":1,
"subject":"0xa805a67f59cccc9869d5dcb4ffc02fc697b5819d","trade":"real-estate-agent"}

(one line of 371 characters, wrapped here only to fit)
05

Identifier

The claim identifier is the SHA-256 of the canonical bytes, lowercase hex. SHA-256 rather than keccak because every language, every shell and every government IT department already has it, and a person checking this by hand should not have to install anything.

claimId = fba0fc855026ba9758a1bbb2a41b3a4a5ebe54be5aca29b07c034d5c0e04ae10

That digest is the real digest of the document in section 2. The tool on the verify page reproduces it in the browser, and the shell command above reproduces it on your machine.

06

Signature

The subject signs EIP-712 typed data over the identifier and the window. The document itself is not signed directly, so a signature can be carried without the document and still bind to it.

claim.sig.jsonEIP-712
EIP-712 typed data

domain = {
  name: "Revow",
  version: "1",
  chainId: 1
}

Claim(
  bytes32 claimId,
  uint64  issuedAt,
  uint64  expiresAt
)

claimId   = sha256(canonical claim document)
issuedAt  = the issued field, as a unix timestamp
expiresAt = the expires field, as a unix timestamp

For the document above, signed by the subject key:

digest    = 0x16553507701ee06320612aa0913000d308a55c7d20197997f247a214e3511f87
signature = 0xfc53b87067d4e4fdfbfadff6746d2992ced057c21305884402e41877f38c8c393e
              e58c490a53c338e03ac954fe55902bd7fcc087fde965d6d0560199466ff3141c

No verifyingContract is set, because a claim is not bound to a deployment. A consumer that recovers an address other than the subject rejects the claim outright.

07

Publication

A claim document and its signature are published wherever the subject chooses: their own domain, a payment page, an object store, a content addressed network, an email attachment. The digest is the name, so the location carries no authority and may change at any time.

08

What a claim does not prove, and known weaknesses

A claim proves that a key made a statement. It proves nothing about the licence. A fraudster can sign a claim quoting a real licence number in under a minute, and the only thing standing between that claim and a reader is the evidence layered on top of it.

The authority field is the weakest part of this document. There is no global naming of issuing bodies, and a lab-maintained list would make Revow a namespace operator, which rule four forbids. The current answer is that consumers match on jurisdiction and trade and treat authority as a hint. This is open problem OP-5 and it is not solved.

Read this page as Markdown