Repository navigation
axt-verify v0.2.0
Upgrade before the log writes a newer leaf field set
The transparency log commits each event under a numbered set of fields, a leaf field set. A release of axt-verify can rebuild only the field sets that existed when it was cut. This release adds field set v2 (version byte 0x02).
- Older copies must be upgraded first. v0.1.0 rebuilds field set v1 only. It reports every event the log writes under v2 as "not what the log committed" and exits 1, whether or not the event carries a newer field. Install this release before the log begins writing v2.
- This release rebuilds v1 and v2. Events the log wrote under v1 keep verifying.
- The log may begin writing v2 at any time after this release.
- If every event after some date fails as "not what the log committed", install the latest release and run again until a pass completes without reporting truncation. A failure that survives that is a security finding.
The README's section Leaf field sets also ship by release says the same, and the action for exit status 1 now points to it.
What changed
- Field set v2. It is field set v1's eleven keys plus one optional key, present only when the event has a value for it. The README's How verification works names the key. A field set is never edited after release: v2 has its own renderer and v1's is unchanged.
- How an event is checked. Nothing the log serves says which field set a leaf was written under, so the served event alone decides what is tried.
- An event that carries a key only v2 commits is checked as a v2 leaf and as nothing else. A value for that key that is added to, removed from or altered in a served event therefore fails.
- Any other event is checked as a v2 leaf first and then as a v1 leaf. A match with either verifies it, because both commit the same eleven values.
- State file. A pending entry can now carry an optional
legacy_leaf_hash. The state file'sversionis unchanged, and a file v0.1.0 wrote still loads. A state file a newer release has written is not for an older release: the older release reads the newer leaf hashes as changed content and fails every event that was awaiting a checkpoint. - One case to know about when upgrading with a state file from v0.1.0. A pending entry that v0.1.0 wrote, and that a pass does not list again (a run cut short by
--max-pages, or an event outside--overlap), is checked from the one hash v0.1.0 stored, which is the v1 hash.- If the log wrote that event's leaf under v2, the entry fails on every pass until a pass lists the event. Raise
--max-pages(the tool says so) or widen--overlap. Editing the state file is the last resort. - If the log wrote it under v1, it verifies as v0.1.0 would have verified it, even when the event is now served with a value that leaf does not commit. The next pass that lists the event checks it again from the served event, and fails it in that case.
- If the log wrote that event's leaf under v2, the entry fails on every pass until a pass lists the event. Raise
- Reports. In
--jsonoutput,events.failed[].reasonfor a content mismatch can now list one mismatch for each field set tried, separated by;. No field name changed. - Unchanged. The built-in log key (SHA-256
1dff5fe420d49743fe444a04fc17f818eea856699dec2ebbc24df15602c74a58), the command names, the flag names, the environment variable names, the exit codes, the--jsonfield names and the dependencies are the same as in v0.1.0. - Test vectors.
leaf/testdata/served_vectors_v2.jsonis new and holds each v2 case beside its v1 form.
For library users: exported identifiers of package leaf
New:
SchemaVersionV2(byte,0x02): the version byte of field set v2.UserProfileIDKey("user_profile_id"): the served key field set v2 adds.Event.Legacy([]byte): the event's v1 entry. It is nil when the event carries a key only v2 commits, because no v1 entry commits one.Event.Hashes() [][]byte: the leaf hashes the event can have in the log, in the order to try them:Entry's, thenLegacy's when there is one.Event.RecordedFrom(stored []byte) bool: whether any release of this tool, including one that knew only v1, would have recordedstoredfrom this serving. It compares servings only and says nothing about what the log holds.
Meaning changed:
Event.Entrywas the v1 entry (SchemaVersionfollowed by the canonical JSON). It is now the v2 entry (SchemaVersionV2followed by the canonical JSON over field set v2). The v1 entry moved toEvent.Legacy.Event.Hash()still returns the leaf hash ofEntry, so it now returns the v2 hash. Code that comparedHash()with a leaf hash from the log does not match a leaf written under v1 any more. UseHashes()and accept a match with any element.Parsenow projects a served event onto field set v2, and onto v1 as well when the event carries no key only v2 commits. It reads the added key with the same strictness as the others: a repeated member name or a name that differs only in case is refused, and a null value reads as absent.Types()returns the same two event types. It is now the closed list that v1 and v2 share.ErrUnknownTypeis the same error value with a new message text: "event type is not one this axt-verify release knows; a newer axt-verify is required".
Unchanged: SchemaVersion (0x01), IndexKey, ClaimsOurType. TypeOf returns what it returned before; because it reads a served event with the same strictness as Parse, it now also refuses an event that has a member whose name differs from the added key only in case.
Outside leaf, in the top-level package: PendingEvent.LegacyLeafHash is new (JSON legacy_leaf_hash, optional).
Install
go install github.com/anthropics/axt-verify/cmd/axt-verify@v0.2.0Requires Go 1.26 or newer.
Third-party licenses
Output of go-licenses report ./... in this tree:
github.com/anthropics/axt-verify,https://github.com/anthropics/axt-verify/blob/HEAD/LICENSE,Apache-2.0
github.com/transparency-dev/formats,https://github.com/transparency-dev/formats/blob/404c0d5b696c/LICENSE,Apache-2.0
github.com/transparency-dev/merkle,https://github.com/transparency-dev/merkle/blob/v0.0.2/LICENSE,Apache-2.0
golang.org/x/mod/sumdb,https://cs.opensource.google/go/x/mod/+/v0.35.0:LICENSE,BSD-3-Clause
The full license texts ship in THIRD_PARTY_NOTICES/ in the repository.