Releases: clearsign-dev/clearsign
Release list
v0.1.2
Unsigned evaluation release. Not approved for high-value custody. This
release fixes review, desktop and packaging problems in v0.1.1. It holds no keys
and does not enforce what a separate wallet signs. Independent post-fix review,
platform code signing and production-hardware validation remain outstanding.
Use public fixtures or disposable test workflows for evaluation.
Release checks - 4 October 2026
- Add native launch checks for the MSI and AppImage, alongside Debian and NSIS.
Remove the installed copy before testing the next format. - Verify the existing candidate manifest before publishing instead of generating
new checksums. Reject stale installer versions, unexpected files, incomplete
checksum lists and mismatched source metadata. - Check version consistency on branch builds too, including npm lock metadata.
- Give oversized desktop IPC requests the same refusal response schema as other
invalid input, and test this at the native Rust boundary.
Fixed - direct review on 3 October 2026
- Backport the upstream glib
VariantStrIterfix for the Linux desktop's GTK3
dependency. The original optimized iterator tests crashed; all 11 pass with
the two-line correction. Verify the vendored source against the original crate. - Test installed Debian and Windows NSIS applications through their native
WebViews, and validate complete release candidates on branch builds. - Enable the Tauri JavaScript bridge required by the desktop backend.
- Declare and generate UTF-8 explicitly: the packaged macOS window previously
misdecoded text and failed to initialize its controls despite browser tests. - Discover Windows Chrome installations and use portable file URLs in the
installer smoke tests. - Bind fetched records to the requested transaction hash, cancel stale network
requests, and prevent delayed file reads from replacing newer input. - Limit network response bytes while streaming, before collecting the body.
- Reject duplicate JSON keys, malformed chain IDs, and absent signed numeric
fields rather than replacing them with defaults. - Gate publishing on tests and version checks; compare canonical binaries with
the committed hashes rather than the build script's freshly written file. - Include installers and the standalone HTML page in SHA256SUMS, publish build
metadata, require every platform's artifacts, and use locked dependencies. - Reject empty allocator test runs and harness errors instead of inferring
success from the absence of failure lines in a log.
This was an AI-assisted code review with regression tests, not an independent
human audit. Hardware validation and independent re-review remain outstanding.
Fixed — two items from a re-check
The cap was tested beside the gate, not at it. The test asserted
too_many_to_acknowledge(), the predicate approve consults — so deleting the
check from approve itself would have left it green. There is now a test in
clearsign-keys/tests/approval_boundary.rs that goes through approve, at the
cap and one over, with both an empty and a complete acknowledgement list.
Checked against the mutation it exists for: removing the gate's check fails it.
The selector qualification named only the first destination. It was
deduplicated on its code, so a batch touching four contracts carried a single
notice naming one of them — which reads as though the limitation applies to that
address and not the other three. There is now one notice per distinct
destination.
Changed — how the review passes are described
The v0.1.1 and v0.1.2 entries said "a second independent review" and "the same
reviewer". Those rounds were AI-assisted review passes, not a human security
audit, and the reviewer said so plainly: they cannot accept payment, enter a
consulting contract, or provide a professional auditor's attestation.
The wording is corrected here, and 03-verification-status.md now separates the
one human review in September from the four AI-assisted passes since. The
second human review is still unmet. It matters that the distinction is made by
us rather than discovered by someone else.
Fixed — the window was broken
MAX_RECORD_BYTES was referenced in three places and declared in none.
Fetching a transaction or dropping a file threw ReferenceError and the page
did nothing. Introduced when input bounding was added: the edit targeted
<script> and the tag is <script type="module">, so it silently never
applied. Every Rust test passed, the build's parse check passed, and the page
shipped broken.
There is now a smoke test — app/smoke-test.mjs — that loads the built page in
a real browser and uses it: reviews the Bybit fixture, drops an oversized file,
edits the input, clears, and fails on anything the page throws. It runs as part
of app/build.sh and in CI. Checked against the bug it exists for: with the
constant removed, three of its checks fail.
Writing it found a second one. The oversized-file branch called show(...),
which is not a function this page has.
A finished review could outlive the input that produced it. The review flow
had no request generation, so a slow fetch could land after a fast one, or after
Clear, and leave a result on screen belonging to a transaction nobody was
looking at. Every review now takes a generation; typing, changing network or
version, clearing and starting another review all move it on, and a reply from
an older generation is dropped.
Changed — the interface says what decoding establishes
The results panel was headed "What it actually does". It is now "What the
bytes say", which is the checkable claim. The WARNING verdict said "Nothing
here is irreversible on its face"; it now says that of the part that could be
decoded, and adds that what the code at the other end does is not established by
these bytes. The clear verdict says plainly that nothing flagged is not the same
as safe.
Changed — how a decoded call is described
A four-byte selector says what shape a call has, not what the code at the
other end will do with it. The reviewer sent a correctly encoded
transfer(address,uint256) to an address that is not a token; ClearSign called
it "ERC-20 transfer", labelled the target "Token contract", and returned exit 0.
Everything it printed was derived from the bytes, but two of the words were not.
The wording now separates the two:
Action ........................ Matches ERC-20 transfer(address,uint256)
Contract called ............... 0x…
Recipient, if it is a token ... 0x…
with an INFO finding, SELECTOR_IS_NOT_BEHAVIOUR, saying that whether the
address is a token and what its code does was not established — a contract can
answer to a familiar selector however it likes, and a proxy can point somewhere
new between one transaction and the next. transfer, approve and
transferFrom all carry it. The exit code is unchanged: refusing every token
transfer would make the tool useless, and the fix here is precision, not alarm.
Added — a support matrix generated from the code
docs/10-what-is-supported.md, written by
signing-core/scripts/support-matrix.sh, separating listed (the code will
act on it), tested (a fixture exercises it) and proven (checked against
something that is not this project).
It exists because the deployment table was documented as 1,404 address-chain
pairs while the code held 2,089. Nobody lied; the table grew and the prose
did not. That is what a hand-written support claim does. The corrected numbers
are 11 deployments, 2,089 pairs across 561 chain IDs, 12 networks in the
application, and Ethereum mainnet as the only chain anything has been proven on
live.
It also records, for the first time in one place, that EVM support does not
mean every Ethereum transaction: types 0x01, 0x03 and 0x04 are refused,
the last being EIP-7702 authorizations. They are refused by name rather than
guessed at, which is the designed behaviour, but the gap was not written down.
Changes since v0.1.1 for users of the authority engine. A further review pass
went through the v0.1.1 fixes and found that one of them was incomplete and one
of the claims made about it was false.
Fixed
An acknowledgement identifier could name two findings. Finding numbers were
u16, produced by clamping at 65,535. Past that every finding carried the same
number, and in the plan engine — where requirements were held in a set — the
duplicates merged. A 128-step plan producing 82,048 required findings collapsed
to 65,536 distinct ones: 16,512 requirements silently disappeared, and the
short list was accepted while the complete one was refused.
Identifiers are now u32 and are never clamped. A review with more findings
than MAX_ACKNOWLEDGEABLE_FINDINGS is refused outright rather than approved
from whatever fits — a list nobody could read through is not a list anybody
approved. The plan engine compares requirements as lists rather than through a
set, so two that look alike stay two.
The transaction signer was affected differently: it compares lists already, so
clamped numbers made a review impossible to approve rather than approvable on a
partial list. Wrong, but wrong in the safe direction.
The old acknowledgement syntax was silently reinterpreted. v0.1.1 changed
authority --ack from naming a step to naming a finding, and accepted the old
#5:CODE form by stripping the #. That turned "step 5" into "finding 5" — a
different finding, approved without comment. The v0.1.1 changelog said the old
form was "refused loudly". That was not true. It is now: the tool refuses it
and explains why, and the command's help no longer teaches the old form.
Input is bounded before it is read. The JSON parser built the whole value
before applying any limit, and the window read a dropped file entirely before
looking at its size. A limit applied after the allocation it exists to prevent
is not a l...
v0.1.1
Status Update: 29 September 2026
Developer preview. Do not use with real signing keys or treat the review as a safety guarantee.
The assets attached here are still v0.1.1. Later parser, acknowledgement and browser-interface fixes on main are not included in these downloads. See the current changelog and verification status before testing.
The follow-up review behind this release was AI-assisted, not a second independent human audit. This version also accepts the legacy leading-# acknowledgement syntax; the original notes incorrectly described that syntax as reliably rejected.
Only the canonical aarch64 Linux binary has the documented reproducibility comparison. Compilation uses a local registry cache; the container bootstrap still downloads dependencies. Desktop installers are unsigned. No assets or tags were replaced by this note update.
Original release notes (superseded claims corrected above)
Update from v0.1.0 if you review batched transactions. A second independent
review found a decoding gap that could hide a dangerous call from you.
Fixed
A dangerous call past the display limit was never named. A Safe MultiSend
batch shows 32 calls and parses up to 1024. The calls past the display limit
were checked for DELEGATECALL and for an invalid operation byte, and then
reported only as "some calls are not shown". A call at position 33 granting an
unlimited token approval, or changing the Safe's owners, threshold or
implementation, got no finding of its own.
v0.1.0 reports such a batch as BLIND, so it still ends in DO NOT SIGN — it was
never silent. But the specific reason was missing, and INV-6 says those
actions are CRITICAL. Every call is now judged by the same rules whether or not
there is room to display it, with the findings grouped by code so a long batch
stays readable.
Two findings sharing a code in one plan step needed only one
acknowledgement. In the authority engine, requirements were keyed on
(step, code), so duplicates merged. Reachable through SignTransaction: a
batch granting unlimited approvals to two different spenders produced two
UNLIMITED_APPROVAL findings, and confirming once approved both — to two
different addresses. Findings are now numbered as the review displays them,
which is what the transaction signer has always done.
This affects authority and the authority command-line tool. The transaction
reviewer was never affected.
The window contacted Google every time it opened. It fetched a webfont from
fonts.googleapis.com on load. For a tool whose value is that it talks to
nothing, that told a third party who was reviewing a transaction, and when. The
request is gone and the font allowances are out of the application's content
security policy.
Corrected claims
"Never touches a network" was too broad. Fetching a queued transaction by
its hash contacts Safe's transaction service, by design. The decoder still never
opens a socket, and the signer image has no network stack compiled into its
kernel — those are the precise statements, and they are stronger than the loose
one.
"Offline" overstated the canonical build. The compile is offline from a
copied registry cache. The container's own bootstrap is not: it installs three
packages with apt and downloads rustup, neither pinned by digest. The
recorded hashes say so now. Pinning the bootstrap is the obvious next step and
has not been done.
Interface change
authority --ack now takes the number beside the finding rather than the step
number: --ack 2:SECRET_EGRESS, not --ack #5:SECRET_EGRESS. A leading # is
still accepted so an acknowledgement copied from an older transcript is refused
loudly rather than misread. Scripts that acknowledged by step number need
updating.
Also
A fuzz target was building its acknowledgements by deduplicating on
(step, code) — the same assumption the engine was making — so it agreed with
the bug and could never have found it. About 6.5 million executions proved
nothing on that path. It now asks the review what it requires.
A tool for reading a Safe transaction before you approve it. It holds no
keys and signs nothing.
The decoder never opens a socket. Paste a transaction and nothing
leaves the machine. The one feature that does reach the network is
fetching a queued transaction by its hash, which contacts Safe's
transaction service and nothing else.
Start with README.md in the archive, or
docs/06-using-it-before-you-sign.md in the repository.
clearsign safe-json tx.json --chain-id 1
What is checked, and what is not. This has had one external security
review: ten findings, all fixed. The fixes have not been reviewed by
anyone outside the project. docs/03-verification-status.md lists what
is proven and what is not, including the gaps.
Reproducibility. The aarch64-unknown-linux-musl build is the one
you can check, and it is built here the same way you would rebuild it:
in the canonical container, offline, with build paths remapped out.
Rebuild it with signing-core/scripts/reproducible-cross-check.sh and
compare against signing-core/EXPECTED-HASHES.txt.
Compare the binary, not the archive. BINARY-SHA256 attached here
is the binary's hash; SHA256SUMS covers the archives, which include a
README and the licences and so hash differently.
The macOS and x86_64 builds are produced by GitHub's runners and are
not reproducible in that sense — rustc does not promise identical
output across compiler hosts, and this project would rather say so than
imply otherwise.
The application is not signed yet. macOS and Windows will warn that
it comes from an unidentified developer, because it does: signing
certificates are a paid registration this project has not taken out.
Until then the honest check is the hash, not the badge.
Not for real keys. The signing and seed commands stay behind a
development guard. Anything typed into an everyday computer should be
treated as exposed.
v0.1.0
Status Update: 29 September 2026
Developer preview. Do not use with real signing keys or treat the review as a safety guarantee.
The assets attached here are still v0.1.0. Later parser, acknowledgement and browser-interface fixes on main are not included in these downloads. See the current changelog and verification status before testing.
This version is superseded by v0.1.1. The original network claim was too broad: desktop Safe-service fetch contacts the network, and this version also fetched a webfont on startup. The decoder itself does not open sockets.
Only the canonical aarch64 Linux binary has the documented reproducibility comparison. Compilation uses a local registry cache; the container bootstrap still downloads dependencies. Desktop installers are unsigned. No assets or tags were replaced by this note update.
Original release notes (superseded claims corrected above)
A tool for reading a Safe transaction before you approve it. It holds no
keys, signs nothing, and never touches a network.
Start with README.md in the archive, or
docs/06-using-it-before-you-sign.md in the repository.
clearsign safe-json tx.json --chain-id 1
What is checked, and what is not. This has had one external security
review: ten findings, all fixed. The fixes have not been reviewed by
anyone outside the project. docs/03-verification-status.md lists what
is proven and what is not, including the gaps.
Reproducibility. The aarch64-unknown-linux-musl build is the one
you can check, and it is built here the same way you would rebuild it:
in the canonical container, offline, with build paths remapped out.
Rebuild it with signing-core/scripts/reproducible-cross-check.sh and
compare against signing-core/EXPECTED-HASHES.txt.
Compare the binary, not the archive. BINARY-SHA256 attached here
is the binary's hash; SHA256SUMS covers the archives, which include a
README and the licences and so hash differently.
The macOS and x86_64 builds are produced by GitHub's runners and are
not reproducible in that sense — rustc does not promise identical
output across compiler hosts, and this project would rather say so than
imply otherwise.
The application is not signed yet. macOS and Windows will warn that
it comes from an unidentified developer, because it does: signing
certificates are a paid registration this project has not taken out.
Until then the honest check is the hash, not the badge.
Not for real keys. The signing and seed commands stay behind a
development guard. Anything typed into an everyday computer should be
treated as exposed.