This document defines a JSON Web Signature (JWS) profile and abstract operations for representing, extending, validating, and merging a directed linear history of signed JSON entries. A valid history is anchored by a root public key and MAY rotate its verification key over time.
This document is an unofficial, AI-assisted draft. It is not a standards-track publication and MAY change, contain errors, or be withdrawn at any time. It is published for implementation experimentation and review feedback.
A history consumer often needs to verify what an issuer intended at a particular time, even when snapshots are exchanged across distributed systems. This document specifies a JOSE-based format and deterministic operations that preserve a single verified linear chain anchored at a root public key.
This specification is format- and algorithm-focused. API shapes used by a specific reference implementation are non-normative, but failure conditions are normative.
Entry payloads are intentionally schema-flexible. State at a point in time is resolved by applying extension members from root to that point in chain order.
aft links from head to root, and then verifying tokens in
root-to-head order from root pk through verified
rot transitions.
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.
JSON is defined by [[!RFC8259]]. JWT and NumericDate are defined by [[!RFC7519]]. JWS and base64url handling in this specification follow [[!RFC7515]]. JWK and JOSE algorithm identifiers are defined by [[!RFC7517]] and [[!RFC7518]].
jti, iss, nbf,
aft, pk, and rot. They are
protocol metadata and are excluded from [=resolved state=]
accumulation.
nbf member interpreted as an issuer time assertion.
It records the UNIX-time NumericDate that the issuer perceived when
authoring the entry and expresses when the issuer intends consumers to
apply that entry, based on each consumer's own perceived UNIX time.
This value supports deterministic inspection and asynchronous
verification workflows; it is not proof of absolute clock truth.
U+0000, used as aft in
exactly one root [=JWH entry=].
pk. It
verifies the root token and initializes the [=active verification
key=].
rot,
that key becomes active for subsequent entries.
A [=JWH entry token=] MUST be a JWS Compact Serialization as defined by [[!RFC7515]].
The JWS Protected Header MUST contain:
typ with the exact value JWT.alg set to a JOSE algorithm identifier from [[!RFC7518]].
The alg value MUST NOT be none. The signing or
verification key used for processing a token MUST be a JWK compatible
with the declared algorithm [[!RFC7517]] [[!RFC7518]].
| Member | Type | Requirements |
|---|---|---|
jti |
string | MUST be non-empty and unique within one [=JSON Web History=] [[!RFC7519]]. |
iss |
string | MUST be non-empty and equal across all entries in one [=JSON Web History=] [[!RFC7519]]. |
nbf |
number | MUST be a NumericDate [[!RFC7519]] and represent [=not before=]. |
aft |
string |
MUST be either [=root pointer=] or the jti of exactly
one earlier entry in the same history.
|
pk |
JSON object (JWK) |
MUST be present when aft equals [=root pointer=] and
MUST be a public JWK compatible with the declared
alg [[!RFC7517]] [[!RFC7518]].
|
rot |
JSON object (JWK) | OPTIONAL. When present, MUST be a public JWK and becomes the [=active verification key=] for the next entry in chain order. |
* (extension members) |
JSON value | MAY contain arbitrary JSON members. This specification imposes no fixed payload schema across history entries. |
Implementations MUST preserve unknown extension members when parsing, validating, inspecting, and merging histories.
A candidate [=JSON Web History=] is valid only if all of the following are true:
aft equal to [=root pointer=].
aft references an existing
jti in the same history.
jti.aft links from root is
acyclic and includes every entry exactly once.
iss value.pk and it is a valid public JWK.
pk and
updated only by verified rot members.
This specification defines the following abstract operations: [=create a JWH entry=], [=tokenize a JWH entry=], [=parse a JWH entry token=], [=verify a JWH entry token=], [=validate a JWH snapshot=], [=start a JWH=], [=extend a JWH=], [=inspect a JWH=], and [=merge JWH snapshots=].
A machine-readable identifier for a specific failure condition. Conforming implementations MUST expose an [=error code=] for every operation failure defined in this specification.
| Code | Failure condition |
|---|---|
TOKEN_INVALID_COMPACT_JWS |
A [=JWH entry token=] is not a compact JWS with exactly three dot-separated parts. |
TOKEN_INVALID_PROTECTED_HEADER |
A protected header is missing required members or violates this specification's JWS profile. |
TOKEN_ALG_NONE_FORBIDDEN |
A token declares alg as none. |
TOKEN_SIGNATURE_VERIFICATION_FAILED |
Signature verification fails for a token. |
HISTORY_EMPTY_SNAPSHOT |
A [=JWH snapshot=] contains no entries. |
HISTORY_ISSUER_MISMATCH |
Entries in one history do not share the same iss.
|
HISTORY_ROOT_KEY_MISSING |
Root entry is missing required pk. |
HISTORY_ROOT_KEY_MISMATCH |
A supplied expected root key does not match root
pk.
|
HISTORY_ROOT_KEY_INVALID |
Root pk is not a valid public JWK compatible with
the chain's verification process.
|
HISTORY_ROTATION_KEY_INVALID |
A rot member is present but not a valid public JWK.
|
ENTRY_INVALID_CLAIMS_OBJECT |
Entry creation input claims is not a JSON object. |
ENTRY_RESERVED_MEMBER_OVERRIDE |
Entry creation input attempts to set a [=reserved entry members=] name through extension members. |
HISTORY_FORK_DETECTED |
More than one child references the same parent entry. |
HISTORY_CHAIN_DISCONNECTED |
The chain is not a single connected root-to-head sequence. |
HISTORY_MERGE_CONFLICTING_JTI |
Two snapshots contain the same jti with different
payloads.
|
This operation takes iss, aft, and extension members object claims and returns a [=JWH entry=].
ENTRY_INVALID_CLAIMS_OBJECT.
entry.jti to a new non-empty unique string.entry.iss to iss.entry.nbf to the current NumericDate.entry.aft to aft.ENTRY_RESERVED_MEMBER_OVERRIDE.
entry[name] to claims[name].This operation takes a [=JWH entry=] and signing key and returns a [=JWH entry token=].
typ set to
JWT and alg set to the key algorithm.
This operation takes a [=JWH entry token=] and returns decoded header, payload, signature bytes, and signing input without validating the signature.
. into three parts.typ to equal JWT.alg to be a non-empty string.This operation takes a [=JWH entry token=] and verification key and returns the verified [=JWH entry=].
alg is none, fail.alg, fail.
This operation takes a [=JWH snapshot=] (or [=JWH string=]) and optionally an expected [=root verification key=] and returns normalized history state.
HISTORY_EMPTY_SNAPSHOT.
jti.entry.iss; otherwise require equality, or fail with
HISTORY_ISSUER_MISMATCH.
jti is already present:
HISTORY_DUPLICATE_JTI.
HISTORY_CONFLICTING_JTI.
jti.aft links.HISTORY_UNORDERED_SNAPSHOT.
pk, fail with
HISTORY_ROOT_KEY_MISSING.
root.pk is not a valid public JWK, fail with
HISTORY_ROOT_KEY_INVALID.
root.pk, fail with
HISTORY_ROOT_KEY_MISMATCH.
root.pk.
rot:
entry.rot is not a valid public JWK, fail
with HISTORY_ROTATION_KEY_INVALID.
entry.rot.
This operation takes a [=JWH string=] and returns a [=JWH snapshot=].
STRING_INVALID_JSON_ARRAY.
STRING_INVALID_SNAPSHOT_TOKEN.
This operation takes iss, extension members object claims, a root signing key, and a root public key, and returns a [=JWH string=].
aft set to [=root pointer=] and claims.
entry.pk to the supplied root public key.This operation appends a new entry to a valid history. It takes an existing history, extension members object claims, a signing key, and optional rotation key nextKey, and returns a [=JWH string=].
iss = head.iss, aft = head.jti, and
claims.
HISTORY_ROTATION_KEY_INVALID.
entry.rot to nextKey.This operation returns the [=resolved state=] at time t.
entry.nbf <= t.
state[name] to value.This operation takes one or more [=JWH snapshot=] values and returns a single merged [=JWH snapshot=].
A merge MUST be accepted whenever the deduplicated token set forms one
directed linear root-to-head graph and validates from root
pk through any verified rot transitions.
HISTORY_MERGE_EMPTY_INPUT.
jti.jti already exists:
HISTORY_MERGE_CONFLICTING_JTI.
aft links.
HISTORY_MERGE_MISSING_TOKEN.
A reference TypeScript implementation can expose these operations as top-level functions and optional convenience classes. Class names and method names are not normative requirements of this specification.
Implementations MUST follow JWT best current practices from
[[!RFC8725]]. In particular, implementations MUST enforce algorithm
allowlists and MUST reject alg=none.
Consumers MUST verify every token signature before relying on entry contents. Treating unverified payload data as trusted history state can enable forgery.
Consumers MUST derive verification keys from root pk and
only from verified rot transitions.
Implementations MUST treat malformed or invalid histories as hard failures and MUST NOT auto-repair by dropping conflicting entries.
This document has no IANA actions.
This specification defines conformance requirements for producers and consumers of [=JSON Web History=] artifacts. Requirements are expressed in this document using the terminology defined in [[#conventions]].