@@ -7,6 +7,173 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
88## [ Unreleased]
99
10+ ## [ 0.6.0] — 2026-04-23
11+
12+ ### Added
13+ - ** Proof format restructure.** The subject-kind discriminator moves
14+ from the string ` s.src ` to a top-level integer ` t ` that identifies
15+ both the subject and every external-commitment chain. The byte is
16+ emitted as a 2-byte big-endian u16 right after the version byte in
17+ the Ed25519 signing pre-image, so flipping ` t ` on a bundle without
18+ re-signing breaks signature verification — real cryptographic
19+ domain separation between subject kinds. Frozen type-code registry
20+ (single source of truth at ` internal/proof/ptype/ptype.go ` ):
21+ - ` 10 ` block · ` 20 ` item · ` 30 ` entropy_nist · ` 31 ` entropy_stellar
22+ · ` 32 ` entropy_bitcoin · ` 40 ` commitment_stellar · ` 41 ` commitment_bitcoin.
23+ The wire field ` v ` remains ` 1 ` ; this is a structure-only refresh,
24+ not a new bundle version. Parser tightened: unknown ` t ` rejected,
25+ ` cx ` required non-empty, block proofs must not carry ` s ` / ` ip ` ,
26+ non-block proofs must, every hash field decodes to the exact byte
27+ count declared by the CDDL schema (32 for hashes, 4 for kid, 32
28+ for pk, 64 for sig). Opaque failures three steps into the pipeline
29+ now surface as "pk must be 32 bytes, got 31" at parse time.
30+ - ** Beacons are first-class proof subjects (` t = 11 ` ).** Beacon and
31+ block (` t = 10 ` ) share the same wire shape (no ` s ` , no ` ip ` ,
32+ ` subject_hash == block_hash ` ) but are cryptographically distinct
33+ because ` t ` is in the signed payload — a block bundle and a beacon
34+ bundle over the same underlying block have different signatures.
35+ Verify dispatches both through the same pipeline via the new
36+ ` ptype.IsBlockLikeSubject ` / ` ProofBundle.IsBlockLike() ` predicates;
37+ the report's Type row reads "Beacon" for ` t=11 ` , "Block" for ` t=10 ` .
38+ - ** ` truestamp beacon {latest,list,get,by-hash} ` ** — read-only client
39+ for the Truestamp Beacons JSON: API (` GET /api/json/beacons/* ` ).
40+ Shared ` --json ` / ` --hash-only ` / ` --silent ` flags, client-side
41+ UUIDv7 and 64-hex validation before the network round-trip, and
42+ typed sentinel errors (` Unauthorized ` , ` NotFound ` , ` BadRequest ` ,
43+ ` RateLimited ` , ` Server ` ) that preserve the server's JSON: API error
44+ envelope. ` truestamp beacon ` alone defaults to ` latest ` .
45+ - ** ` truestamp download --type ` ** extended to the full six-value
46+ enum matching the server's strict ` /proof/generate ` contract:
47+ ` item | entropy_nist | entropy_stellar | entropy_bitcoin | block | beacon ` .
48+ No ` auto ` , no bare ` entropy ` . Smart default from id shape: a ULID
49+ id defaults to ` --type item ` (the only unambiguous case); a UUIDv7
50+ id without ` --type ` fails fast listing the five valid choices,
51+ since the server cannot infer the subtype from id alone. Downloaded
52+ filenames use the ` truestamp-<stem>-<id>.<ext> ` convention with
53+ hyphenated stems (` truestamp-entropy-nist-<id>.json ` ) while the
54+ wire value stays underscored to match the server enum.
55+ - ** ` truestamp verify --type ` ** — assert the expected subject type
56+ locally and remotely, defending against swapped-file confused-deputy
57+ scenarios (block and beacon are wire-identical apart from the ` t `
58+ byte, so both verify cleanly on their own). Local mode surfaces a
59+ mismatch as a "Subject Type" failure step in the report. Remote
60+ mode forwards the value to the server's ` /proof/verify type ` arg;
61+ the server rejects with HTTP 4xx + ` meta.code=subject_type_mismatch `
62+ if the posted bundle's ` t ` disagrees. When ` --type ` is omitted the
63+ CLI infers it from the input filename / URL basename using the
64+ download convention (` truestamp-beacon-019d…json ` → beacon), prints
65+ a faint stderr hint so the inference is traceable in transcripts,
66+ and lets an explicit ` --type ` override. Inference goes through a
67+ dedicated fuzz target (` FuzzInferTypeFromFilename ` ) with a 20-seed
68+ corpus.
69+ - ** Live entropy-source consistency checks.** The Entropy Source
70+ verification step now contacts the canonical upstream source for
71+ each subject type and byte-compares the bundle's stored value:
72+ - NIST Beacon (` t=30 ` ) — ` beacon.nist.gov/beacon/2.0/chain/{c}/pulse/{p} ` ,
73+ compare ` outputValue ` and ` timeStamp ` .
74+ - Stellar ledger (` t=31 ` ) — Horizon ` /ledgers/{seq} ` on the network
75+ derived from the bundle's Stellar commitment; compare ` hash ` and
76+ ` closed_at ` .
77+ - Bitcoin block (` t=32 ` ) — ` blockstream.info/api/block/{hash} ` pinned
78+ to mainnet (the authoritative public-randomness source, even in
79+ dev deployments that commit to regtest/testnet); compare ` height `
80+ and ` time ` .
81+ Skipped under ` --skip-external ` .
82+ - ** Dual Details + Verify URLs on every post-action card** (beacon,
83+ download, create, and the verify report's Proof section). URL shape
84+ comes from the shared ` ui.SubjectDetailURL ` / ` ui.SubjectVerifyURL `
85+ / ` ui.BeaconDetailURL ` / ` ui.BeaconVerifyURL ` helpers, routed
86+ through one ` publicWebBase ` that strips a trailing ` /api/json ` .
87+ URLs render unconditionally — localhost, 127.0.0.1, and plain-http
88+ hosts too — so the links are visible when developing against a
89+ local server.
90+ - ` internal/beacons ` — HTTP client for the Beacons JSON: API (Latest /
91+ List / Get / ByHash) with typed errors, 429 ` Retry-After ` surfacing,
92+ and a fuzz suite over the parser + UUIDv7 / 64-hex validators.
93+
94+ ### Changed
95+ - ** Homegrown semver parser replaced with ` golang.org/x/mod/semver ` **
96+ (the same package ` go mod ` uses). ~ 100 lines of parser + comparator
97+ deleted; pre-release identifier comparison (numeric vs alphanumeric,
98+ shortest-prefix rules from SemVer §11) now comes from battle-tested
99+ upstream code. Public API (` Semver ` , ` ParseSemver ` , ` Compare ` ,
100+ ` Display ` , ` IsPreRelease ` ) is preserved. ` go.mod ` gains a direct
101+ ` golang.org/x/mod ` dependency — already transitive, so effectively
102+ zero footprint growth in real-world installs.
103+ - ** Upgrade-check no longer mis-ranks git-describe dev builds.** A
104+ locally-built ` 0.5.0-4-g356ee75-dirty ` binary is conceptually
105+ 4 commits AHEAD of v0.5.0, but SemVer §11 ranks it as a pre-release
106+ BELOW the tag. Two concrete fixes: new
107+ ` selfupgrade.IsGitDescribeDev ` predicate regex-detects the
108+ ` <N>-g<SHA>[-dirty] ` shape (accepts bare post-tag and post-release
109+ suffixes, rejects plain pre-releases like ` rc.1 ` ); new
110+ ` selfupgrade.UpgradeAvailable ` delegates to the normal comparator
111+ but compares MAJOR.MINOR.PATCH cores only for dev builds, so
112+ ` 0.5.0-4-g...-dirty → v0.5.0 ` reports no upgrade (would downgrade)
113+ while → v0.5.1 / v0.6.0 / v1.0.0 still does. Both `truestamp
114+ upgrade --check` and the passive upgrade-check nag now use this
115+ predicate.
116+ - ** Narrow-TTY layout fix.** ` lipgloss.JoinVertical(Left, …) ` pads
117+ every line to the widest line across all inputs; one over-wide
118+ section pushed every other row past the terminal width, which the
119+ terminal then hard-wrapped, producing phantom blank lines after
120+ every table row. Switched to ` strings.Join(…, "\n") ` at six call
121+ sites (` internal/verify/presenter.go ` , ` cmd/config.go ` ,
122+ ` cmd/beacon.go ` , ` cmd/beacon_list.go ` , ` cmd/create.go ` ,
123+ ` cmd/download.go ` ). Each site carries a comment pointing back to
124+ the root-cause note.
125+ - ** Tight vertical spacing across every card.** ` lipgloss.HiddenBorder() `
126+ emits invisible top/bottom border rows that stack with explicit
127+ ` "" ` separators in ` strings.Join ` , doubling the apparent gap
128+ between a section header and its first row. New
129+ ` ui.CompactTable() ` helper returns a table with ` HiddenBorder `
130+ plus ` BorderTop/Bottom/Left/Right(false) ` — content is flush to
131+ whatever comes before and after, and callers control inter-section
132+ spacing explicitly. Applied at 16 call sites across ` cmd/ ` and
133+ ` internal/verify/presenter.go ` .
134+ - ** Full-hash display everywhere.** Removed ` truncateHash() ` and its
135+ seven call sites in ` internal/verify/presenter.go ` ; diagnostic
136+ contexts (hash-mismatch diffs, entropy-source mismatch, Bitcoin
137+ fetch/height errors) now emit the full 64-char hex. Two different
138+ hashes sharing prefix + suffix used to read as visually identical
139+ — no longer. Commitments section already did, so this is the
140+ convergence pass. ` cmd/beacon_list.go ` also drops its local
141+ ` truncateHashShort ` in favour of TTY-aware width logic.
142+ - ** Centralized timestamp truncation.** Promoted the package-local
143+ ` verify.truncateToSecond() ` to exported ` ui.TruncateToSecond() ` ,
144+ applied at every human-display site (beacon list TIMESTAMP column,
145+ beacon card, verify report Timeline section). ` --json ` output and
146+ timestamp-extraction sites (` convert time ` , ` convert id ` ) keep full
147+ microsecond precision; ` verify ` Stellar ` closed_at ` / Bitcoin ` time `
148+ mismatch errors keep full precision so a sub-second diff isn't
149+ masked.
150+ - ** Stellar ` net ` strict.** Only ` "testnet" ` and ` "public" ` accepted
151+ — the previous tolerance of other values is gone.
152+ - ** Legacy ` s.kid == b.kid ` equality check removed.** Legitimate key
153+ rotation can produce divergent kids; subject-kid tampering is
154+ still detected because ` kid ` is an input to the 0x13 / 0x23
155+ composite hash.
156+ - Verify report groups renamed to match the authoritative spec:
157+ Subject Data, Block Hash, Epoch Proof, Temporal Window,
158+ Entropy Source.
159+ - Subject-card URL rows now render as rows of the SAME table as
160+ labels (Details / Verify inherit the right-aligned-label column),
161+ and the ` → ` arrow between label and URL was dropped as redundant
162+ visual noise.
163+ - CBOR parser now accepts ` t ∈ {10, 11} ` for no-` s ` / no-` ip ` proofs
164+ matching the JSON parser. Marshaller emits no ` s ` / ` ip ` for both.
165+
166+ ### Removed
167+ - ` --type auto ` and bare ` --type entropy ` on ` download ` — rejected
168+ client-side before any I/O. The server's strict six-value enum is
169+ the contract.
170+ - Old ` s.src ` string discriminator. All callers branch on the ` t `
171+ integer exclusively.
172+
173+ ### Security
174+ - Build pulls ` golang.org/x/mod ` direct; ` task vuln-check ` clean.
175+ - Dependabot: ` github-actions ` group updates ([ #4 ] ( https://github.com/truestamp/truestamp-cli/pull/4 ) ).
176+
10177## [ 0.5.0] — 2026-04-21
11178
12179### Added
@@ -473,7 +640,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
473640 v0.1.0 is the first release of a standalone Go codebase; the two share
474641 nothing beyond the repository name.
475642
476- [ Unreleased ] : https://github.com/truestamp/truestamp-cli/compare/v0.5.0...HEAD
643+ [ Unreleased ] : https://github.com/truestamp/truestamp-cli/compare/v0.6.0...HEAD
644+ [ 0.6.0 ] : https://github.com/truestamp/truestamp-cli/releases/tag/v0.6.0
477645[ 0.5.0 ] : https://github.com/truestamp/truestamp-cli/releases/tag/v0.5.0
478646[ 0.4.0 ] : https://github.com/truestamp/truestamp-cli/releases/tag/v0.4.0
479647[ 0.3.3 ] : https://github.com/truestamp/truestamp-cli/releases/tag/v0.3.3
0 commit comments